1// Copyright (c) 2012 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#include <vector>
6
7#include "base/bind.h"
8#include "base/command_line.h"
9#include "base/memory/weak_ptr.h"
10#include "base/message_loop/message_loop.h"
11#include "chrome/browser/extensions/component_loader.h"
12#include "chrome/browser/extensions/extension_apitest.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/speech/extension_api/tts_extension_api.h"
15#include "chrome/browser/speech/tts_controller.h"
16#include "chrome/browser/speech/tts_platform.h"
17#include "chrome/common/chrome_switches.h"
18#include "extensions/browser/extension_system.h"
19#include "net/base/network_change_notifier.h"
20#include "testing/gmock/include/gmock/gmock.h"
21#include "testing/gtest/include/gtest/gtest.h"
22
23// Needed for CreateFunctor.
24#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
25#include "testing/gmock_mutant.h"
26
27using ::testing::AnyNumber;
28using ::testing::CreateFunctor;
29using ::testing::DoAll;
30using ::testing::Invoke;
31using ::testing::InSequence;
32using ::testing::InvokeWithoutArgs;
33using ::testing::Return;
34using ::testing::SaveArg;
35using ::testing::SetArgPointee;
36using ::testing::StrictMock;
37using ::testing::_;
38
39namespace {
40int g_saved_utterance_id;
41}
42
43namespace extensions {
44
45class MockTtsPlatformImpl : public TtsPlatformImpl {
46 public:
47  MockTtsPlatformImpl()
48      : ptr_factory_(this),
49        should_fake_get_voices_(false) {}
50
51  virtual bool PlatformImplAvailable() {
52    return true;
53  }
54
55  MOCK_METHOD5(Speak,
56               bool(int utterance_id,
57                    const std::string& utterance,
58                    const std::string& lang,
59                    const VoiceData& voice,
60                    const UtteranceContinuousParameters& params));
61
62  MOCK_METHOD0(StopSpeaking, bool(void));
63
64  MOCK_METHOD0(Pause, void(void));
65
66  MOCK_METHOD0(Resume, void(void));
67
68  MOCK_METHOD0(IsSpeaking, bool(void));
69
70  // Fake this method to add a native voice.
71  void GetVoices(std::vector<VoiceData>* voices) {
72    if (!should_fake_get_voices_)
73      return;
74
75    VoiceData voice;
76    voice.name = "TestNativeVoice";
77    voice.native = true;
78    voice.lang = "en-GB";
79    voice.events.insert(TTS_EVENT_START);
80    voice.events.insert(TTS_EVENT_END);
81    voices->push_back(voice);
82  }
83
84  void set_should_fake_get_voices(bool val) { should_fake_get_voices_ = val; }
85
86  void SetErrorToEpicFail() {
87    set_error("epic fail");
88  }
89
90  void SendEndEventOnSavedUtteranceId() {
91    base::MessageLoop::current()->PostDelayedTask(
92        FROM_HERE, base::Bind(
93            &MockTtsPlatformImpl::SendEvent,
94            ptr_factory_.GetWeakPtr(),
95            false, g_saved_utterance_id, TTS_EVENT_END, 0, std::string()),
96        base::TimeDelta());
97  }
98
99  void SendEndEvent(int utterance_id,
100                    const std::string& utterance,
101                    const std::string& lang,
102                    const VoiceData& voice,
103                    const UtteranceContinuousParameters& params) {
104    base::MessageLoop::current()->PostDelayedTask(
105        FROM_HERE, base::Bind(
106            &MockTtsPlatformImpl::SendEvent,
107            ptr_factory_.GetWeakPtr(),
108            false, utterance_id, TTS_EVENT_END, utterance.size(),
109            std::string()),
110        base::TimeDelta());
111  }
112
113  void SendEndEventWhenQueueNotEmpty(
114      int utterance_id,
115      const std::string& utterance,
116      const std::string& lang,
117      const VoiceData& voice,
118      const UtteranceContinuousParameters& params) {
119    base::MessageLoop::current()->PostDelayedTask(
120        FROM_HERE, base::Bind(
121            &MockTtsPlatformImpl::SendEvent,
122            ptr_factory_.GetWeakPtr(),
123            true, utterance_id, TTS_EVENT_END, utterance.size(), std::string()),
124        base::TimeDelta());
125  }
126
127  void SendWordEvents(int utterance_id,
128                      const std::string& utterance,
129                      const std::string& lang,
130                      const VoiceData& voice,
131                      const UtteranceContinuousParameters& params) {
132    for (int i = 0; i < static_cast<int>(utterance.size()); i++) {
133      if (i == 0 || utterance[i - 1] == ' ') {
134        base::MessageLoop::current()->PostDelayedTask(
135            FROM_HERE, base::Bind(
136                &MockTtsPlatformImpl::SendEvent,
137                ptr_factory_.GetWeakPtr(),
138                false, utterance_id, TTS_EVENT_WORD, i,
139                std::string()),
140            base::TimeDelta());
141      }
142    }
143  }
144
145  void SendEvent(bool wait_for_non_empty_queue,
146                 int utterance_id,
147                 TtsEventType event_type,
148                 int char_index,
149                 const std::string& message) {
150    TtsController* controller = TtsController::GetInstance();
151    if (wait_for_non_empty_queue && controller->QueueSize() == 0) {
152      base::MessageLoop::current()->PostDelayedTask(
153          FROM_HERE, base::Bind(
154              &MockTtsPlatformImpl::SendEvent,
155              ptr_factory_.GetWeakPtr(),
156              true, utterance_id, event_type, char_index, message),
157          base::TimeDelta::FromMilliseconds(100));
158      return;
159    }
160
161    controller->OnTtsEvent(utterance_id, event_type, char_index, message);
162  }
163
164 private:
165  base::WeakPtrFactory<MockTtsPlatformImpl> ptr_factory_;
166  bool should_fake_get_voices_;
167};
168
169class FakeNetworkOnlineStateForTest : public net::NetworkChangeNotifier {
170 public:
171  explicit FakeNetworkOnlineStateForTest(bool online) : online_(online) {}
172  virtual ~FakeNetworkOnlineStateForTest() {}
173
174  virtual ConnectionType GetCurrentConnectionType() const OVERRIDE {
175    return online_ ?
176        net::NetworkChangeNotifier::CONNECTION_ETHERNET :
177        net::NetworkChangeNotifier::CONNECTION_NONE;
178  }
179
180 private:
181  bool online_;
182  DISALLOW_COPY_AND_ASSIGN(FakeNetworkOnlineStateForTest);
183};
184
185class TtsApiTest : public ExtensionApiTest {
186 public:
187  virtual void SetUpInProcessBrowserTestFixture() {
188    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
189    TtsController::GetInstance()->SetPlatformImpl(&mock_platform_impl_);
190  }
191
192 protected:
193  StrictMock<MockTtsPlatformImpl> mock_platform_impl_;
194};
195
196IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakOptionalArgs) {
197  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
198
199  InSequence s;
200  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
201      .WillOnce(Return(true));
202  EXPECT_CALL(mock_platform_impl_, Speak(_, "", _, _, _))
203      .WillOnce(Return(true));
204  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
205      .WillOnce(Return(true));
206  EXPECT_CALL(mock_platform_impl_, Speak(_, "Alpha", _, _, _))
207      .WillOnce(Return(true));
208  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
209      .WillOnce(Return(true));
210  EXPECT_CALL(mock_platform_impl_, Speak(_, "Bravo", _, _, _))
211      .WillOnce(Return(true));
212  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
213      .WillOnce(Return(true));
214  EXPECT_CALL(mock_platform_impl_, Speak(_, "Charlie", _, _, _))
215      .WillOnce(Return(true));
216  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
217      .WillOnce(Return(true));
218  EXPECT_CALL(mock_platform_impl_, Speak(_, "Echo", _, _, _))
219      .WillOnce(Return(true));
220  ASSERT_TRUE(RunExtensionTest("tts/optional_args")) << message_;
221}
222
223IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakFinishesImmediately) {
224  InSequence s;
225  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
226  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
227      .WillOnce(Return(true));
228  EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _))
229      .WillOnce(DoAll(
230          Invoke(&mock_platform_impl_,
231                 &MockTtsPlatformImpl::SendEndEvent),
232          Return(true)));
233  ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_;
234}
235
236IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakInterrupt) {
237  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
238
239  // One utterance starts speaking, and then a second interrupts.
240  InSequence s;
241  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
242      .WillOnce(Return(true));
243  EXPECT_CALL(mock_platform_impl_, Speak(_, "text 1", _, _, _))
244      .WillOnce(Return(true));
245  // Expect the second utterance and allow it to finish.
246  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
247      .WillOnce(Return(true));
248  EXPECT_CALL(mock_platform_impl_, Speak(_, "text 2", _, _, _))
249      .WillOnce(DoAll(
250          Invoke(&mock_platform_impl_,
251                 &MockTtsPlatformImpl::SendEndEvent),
252          Return(true)));
253  ASSERT_TRUE(RunExtensionTest("tts/interrupt")) << message_;
254}
255
256IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakQueueInterrupt) {
257  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
258
259  // In this test, two utterances are queued, and then a third
260  // interrupts. Speak(, _) never gets called on the second utterance.
261  InSequence s;
262  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
263      .WillOnce(Return(true));
264  EXPECT_CALL(mock_platform_impl_, Speak(_, "text 1", _, _, _))
265      .WillOnce(Return(true));
266  // Don't expect the second utterance, because it's queued up and the
267  // first never finishes.
268  // Expect the third utterance and allow it to finish successfully.
269  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
270      .WillOnce(Return(true));
271  EXPECT_CALL(mock_platform_impl_, Speak(_, "text 3", _, _, _))
272      .WillOnce(DoAll(
273          Invoke(&mock_platform_impl_,
274                 &MockTtsPlatformImpl::SendEndEvent),
275          Return(true)));
276  ASSERT_TRUE(RunExtensionTest("tts/queue_interrupt")) << message_;
277}
278
279IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakEnqueue) {
280  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
281
282  InSequence s;
283  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
284      .WillOnce(Return(true));
285  EXPECT_CALL(mock_platform_impl_, Speak(_, "text 1", _, _, _))
286      .WillOnce(DoAll(
287          Invoke(&mock_platform_impl_,
288                 &MockTtsPlatformImpl::SendEndEventWhenQueueNotEmpty),
289          Return(true)));
290  EXPECT_CALL(mock_platform_impl_, Speak(_, "text 2", _, _, _))
291      .WillOnce(DoAll(
292          Invoke(&mock_platform_impl_,
293                 &MockTtsPlatformImpl::SendEndEvent),
294          Return(true)));
295  ASSERT_TRUE(RunExtensionTest("tts/enqueue")) << message_;
296}
297
298IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakError) {
299  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
300      .Times(AnyNumber());
301
302  InSequence s;
303  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
304      .WillOnce(Return(true));
305  EXPECT_CALL(mock_platform_impl_, Speak(_, "first try", _, _, _))
306      .WillOnce(DoAll(
307          InvokeWithoutArgs(
308              CreateFunctor(&mock_platform_impl_,
309                            &MockTtsPlatformImpl::SetErrorToEpicFail)),
310          Return(false)));
311  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
312      .WillOnce(Return(true));
313  EXPECT_CALL(mock_platform_impl_, Speak(_, "second try", _, _, _))
314      .WillOnce(DoAll(
315          Invoke(&mock_platform_impl_,
316                 &MockTtsPlatformImpl::SendEndEvent),
317          Return(true)));
318  ASSERT_TRUE(RunExtensionTest("tts/speak_error")) << message_;
319}
320
321IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformWordCallbacks) {
322  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
323
324  InSequence s;
325  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
326      .WillOnce(Return(true));
327  EXPECT_CALL(mock_platform_impl_, Speak(_, "one two three", _, _, _))
328      .WillOnce(DoAll(
329          Invoke(&mock_platform_impl_,
330                 &MockTtsPlatformImpl::SendWordEvents),
331          Invoke(&mock_platform_impl_,
332                 &MockTtsPlatformImpl::SendEndEvent),
333          Return(true)));
334  ASSERT_TRUE(RunExtensionTest("tts/word_callbacks")) << message_;
335}
336
337IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformPauseResume) {
338  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
339      .Times(AnyNumber());
340
341  InSequence s;
342  EXPECT_CALL(mock_platform_impl_, Speak(_, "test 1", _, _, _))
343      .WillOnce(DoAll(
344          Invoke(&mock_platform_impl_,
345                 &MockTtsPlatformImpl::SendEndEvent),
346          Return(true)));
347  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
348      .WillOnce(Return(true));
349  EXPECT_CALL(mock_platform_impl_, Speak(_, "test 2", _, _, _))
350      .WillOnce(DoAll(
351          SaveArg<0>(&g_saved_utterance_id),
352          Return(true)));
353  EXPECT_CALL(mock_platform_impl_, Pause());
354  EXPECT_CALL(mock_platform_impl_, Resume())
355      .WillOnce(
356          InvokeWithoutArgs(
357              &mock_platform_impl_,
358              &MockTtsPlatformImpl::SendEndEventOnSavedUtteranceId));
359  ASSERT_TRUE(RunExtensionTest("tts/pause_resume")) << message_;
360}
361
362//
363// TTS Engine tests.
364//
365
366IN_PROC_BROWSER_TEST_F(TtsApiTest, RegisterEngine) {
367  mock_platform_impl_.set_should_fake_get_voices(true);
368
369  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
370      .Times(AnyNumber());
371  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
372      .WillRepeatedly(Return(true));
373
374  {
375    InSequence s;
376    EXPECT_CALL(mock_platform_impl_, Speak(_, "native speech", _, _, _))
377      .WillOnce(DoAll(
378          Invoke(&mock_platform_impl_,
379                 &MockTtsPlatformImpl::SendEndEvent),
380          Return(true)));
381    EXPECT_CALL(mock_platform_impl_, Speak(_, "native speech 2", _, _, _))
382      .WillOnce(DoAll(
383          Invoke(&mock_platform_impl_,
384                 &MockTtsPlatformImpl::SendEndEvent),
385          Return(true)));
386    EXPECT_CALL(mock_platform_impl_, Speak(_, "native speech 3", _, _, _))
387      .WillOnce(DoAll(
388          Invoke(&mock_platform_impl_,
389                 &MockTtsPlatformImpl::SendEndEvent),
390          Return(true)));
391  }
392
393  ASSERT_TRUE(RunExtensionTest("tts_engine/register_engine")) << message_;
394}
395
396IN_PROC_BROWSER_TEST_F(TtsApiTest, EngineError) {
397  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
398  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
399      .WillRepeatedly(Return(true));
400
401  ASSERT_TRUE(RunExtensionTest("tts_engine/engine_error")) << message_;
402}
403
404IN_PROC_BROWSER_TEST_F(TtsApiTest, EngineWordCallbacks) {
405  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
406  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
407      .WillRepeatedly(Return(true));
408
409  ASSERT_TRUE(RunExtensionTest("tts_engine/engine_word_callbacks")) << message_;
410}
411
412IN_PROC_BROWSER_TEST_F(TtsApiTest, LangMatching) {
413  EXPECT_CALL(mock_platform_impl_, IsSpeaking());
414  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
415      .WillRepeatedly(Return(true));
416
417  ASSERT_TRUE(RunExtensionTest("tts_engine/lang_matching")) << message_;
418}
419
420IN_PROC_BROWSER_TEST_F(TtsApiTest, NetworkSpeechEngine) {
421  // Simulate online network state.
422  net::NetworkChangeNotifier::DisableForTest disable_for_test;
423  FakeNetworkOnlineStateForTest fake_online_state(true);
424
425  ExtensionService* service = extensions::ExtensionSystem::Get(
426      profile())->extension_service();
427  service->component_loader()->AddNetworkSpeechSynthesisExtension();
428  ASSERT_TRUE(RunExtensionTest("tts_engine/network_speech_engine")) << message_;
429}
430
431IN_PROC_BROWSER_TEST_F(TtsApiTest, NoNetworkSpeechEngineWhenOffline) {
432  // Simulate offline network state.
433  net::NetworkChangeNotifier::DisableForTest disable_for_test;
434  FakeNetworkOnlineStateForTest fake_online_state(false);
435
436  ExtensionService* service = extensions::ExtensionSystem::Get(
437      profile())->extension_service();
438  service->component_loader()->AddNetworkSpeechSynthesisExtension();
439  // Test should fail when offline.
440  ASSERT_FALSE(RunExtensionTest("tts_engine/network_speech_engine"));
441}
442
443// http://crbug.com/122474
444IN_PROC_BROWSER_TEST_F(TtsApiTest, EngineApi) {
445  ASSERT_TRUE(RunExtensionTest("tts_engine/engine_api")) << message_;
446}
447
448}  // namespace extensions
449