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