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