Home | History | Annotate | Download | only in extension_api
      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 
     27 using ::testing::AnyNumber;
     28 using ::testing::CreateFunctor;
     29 using ::testing::DoAll;
     30 using ::testing::Invoke;
     31 using ::testing::InSequence;
     32 using ::testing::InvokeWithoutArgs;
     33 using ::testing::Return;
     34 using ::testing::SaveArg;
     35 using ::testing::SetArgPointee;
     36 using ::testing::StrictMock;
     37 using ::testing::_;
     38 
     39 namespace {
     40 int g_saved_utterance_id;
     41 }
     42 
     43 namespace extensions {
     44 
     45 class 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 
    169 class 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 
    185 class 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 
    196 IN_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 
    223 IN_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 
    236 IN_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 
    256 IN_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 
    279 IN_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 
    298 IN_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 
    321 IN_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 
    337 IN_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 
    366 IN_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 
    396 IN_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 
    404 IN_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 
    412 IN_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 
    420 IN_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 
    431 IN_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
    444 IN_PROC_BROWSER_TEST_F(TtsApiTest, EngineApi) {
    445   ASSERT_TRUE(RunExtensionTest("tts_engine/engine_api")) << message_;
    446 }
    447 
    448 }  // namespace extensions
    449