// Copyright (c) 2010 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_tts_api.h"
#include "chrome/common/chrome_switches.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

// Needed for CreateFunctor.
#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING
#include "testing/gmock_mutant.h"

#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/cros/cros_mock.h"
#endif

using ::testing::AnyNumber;
using ::testing::CreateFunctor;
using ::testing::DoAll;
using ::testing::InSequence;
using ::testing::InvokeWithoutArgs;
using ::testing::Return;
using ::testing::StrictMock;
using ::testing::_;

class MockExtensionTtsPlatformImpl : public ExtensionTtsPlatformImpl {
 public:
  MOCK_METHOD6(Speak,
               bool(const std::string& utterance,
                    const std::string& locale,
                    const std::string& gender,
                    double rate,
                    double pitch,
                    double volume));
  MOCK_METHOD0(StopSpeaking, bool(void));
  MOCK_METHOD0(IsSpeaking, bool(void));

  void SetErrorToEpicFail() {
    set_error("epic fail");
  }
};

class TtsApiTest : public ExtensionApiTest {
 public:
  virtual void SetUpCommandLine(CommandLine* command_line) {
    ExtensionApiTest::SetUpCommandLine(command_line);
    command_line->AppendSwitch(switches::kEnableExperimentalExtensionApis);
  }

  virtual void SetUpInProcessBrowserTestFixture() {
    ExtensionApiTest::SetUpInProcessBrowserTestFixture();
    ExtensionTtsController::GetInstance()->SetPlatformImpl(
        &mock_platform_impl_);
  }

 protected:
  StrictMock<MockExtensionTtsPlatformImpl> mock_platform_impl_;
};

IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakFinishesImmediately) {
  InSequence s;
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(false));
  ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_;
}

IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakKeepsSpeakingTwice) {
  InSequence s;
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  ASSERT_TRUE(RunExtensionTest("tts/speak_once")) << message_;
}

IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakInterrupt) {
  // One utterances starts speaking, and then a second interrupts.
  InSequence s;
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _))
      .WillOnce(Return(true));

  // Ensure that the first utterance keeps going until it's interrupted.
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .Times(AnyNumber())
      .WillRepeatedly(Return(true));

  // Expect the second utterance and allow it to continue for two calls to
  // IsSpeaking and then finish successfully.
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak("text 2", _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  ASSERT_TRUE(RunExtensionTest("tts/interrupt")) << message_;
}

IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakQueueInterrupt) {
  // In this test, two utterances are queued, and then a third
  // interrupts. Speak() never gets called on the second utterance.
  InSequence s;
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _))
      .WillOnce(Return(true));

  // Ensure that the first utterance keeps going until it's interrupted.
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .Times(AnyNumber())
      .WillRepeatedly(Return(true));

  // Expect the third utterance and allow it to continue for two calls to
  // IsSpeaking and then finish successfully.
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak("text 3", _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  ASSERT_TRUE(RunExtensionTest("tts/queue_interrupt")) << message_;
}

IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakEnqueue) {
  InSequence s;
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak("text 1", _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  EXPECT_CALL(mock_platform_impl_, Speak("text 2", _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(true))
      .WillOnce(Return(true))
      .WillOnce(Return(false));
  ASSERT_TRUE(RunExtensionTest("tts/enqueue")) << message_;
}

IN_PROC_BROWSER_TEST_F(TtsApiTest, PlatformSpeakError) {
  InSequence s;
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(false));
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _))
      .WillOnce(DoAll(
          InvokeWithoutArgs(
              CreateFunctor(&mock_platform_impl_,
                            &MockExtensionTtsPlatformImpl::SetErrorToEpicFail)),
          Return(false)));
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, Speak(_, _, _, _, _, _))
      .WillOnce(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillOnce(Return(false));
  ASSERT_TRUE(RunExtensionTest("tts/speak_error")) << message_;
}

#if defined(OS_WIN)
// Flakily fails on Windows: http://crbug.com/70198
#define MAYBE_Provide FLAKY_Provide
#else
#define MAYBE_Provide Provide
#endif
IN_PROC_BROWSER_TEST_F(TtsApiTest, MAYBE_Provide) {
  EXPECT_CALL(mock_platform_impl_, StopSpeaking())
      .WillRepeatedly(Return(true));
  EXPECT_CALL(mock_platform_impl_, IsSpeaking())
      .WillRepeatedly(Return(false));

  {
    InSequence s;
    EXPECT_CALL(mock_platform_impl_, Speak("native speech", _, _, _, _, _))
        .WillOnce(Return(true));
    EXPECT_CALL(mock_platform_impl_, Speak("native speech 2", _, _, _, _, _))
        .WillOnce(Return(true));
    EXPECT_CALL(mock_platform_impl_, Speak("native speech 3", _, _, _, _, _))
        .WillOnce(Return(true));
  }

  ASSERT_TRUE(RunExtensionTest("tts/provide")) << message_;
}

#if defined(OS_CHROMEOS)
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TtsChromeOs) {
  CommandLine::ForCurrentProcess()->AppendSwitch(
      switches::kEnableExperimentalExtensionApis);

  chromeos::CrosMock crosMock;
  crosMock.InitMockSpeechSynthesisLibrary();
  crosMock.SetSpeechSynthesisLibraryExpectations();

  ASSERT_TRUE(RunExtensionTest("tts/chromeos")) << message_;
}
#endif