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