diff options
24 files changed, 607 insertions, 66 deletions
diff --git a/chrome/browser/chromeos/cros/cros_mock.cc b/chrome/browser/chromeos/cros/cros_mock.cc index ab33dc0..6e7844f 100644 --- a/chrome/browser/chromeos/cros/cros_mock.cc +++ b/chrome/browser/chromeos/cros/cros_mock.cc @@ -29,6 +29,7 @@ namespace chromeos { using ::testing::AnyNumber; +using ::testing::InSequence; using ::testing::InvokeWithoutArgs; using ::testing::Return; using ::testing::ReturnRef; @@ -378,17 +379,20 @@ void CrosMock::SetPowerLibraryExpectations() { } void CrosMock::SetSpeechSynthesisLibraryExpectations() { + InSequence s; + EXPECT_CALL(*mock_speech_synthesis_library_, StopSpeaking()) + .WillOnce(Return(true)) + .RetiresOnSaturation(); EXPECT_CALL(*mock_speech_synthesis_library_, Speak(_)) - .Times(1) .WillOnce(Return(true)) .RetiresOnSaturation(); EXPECT_CALL(*mock_speech_synthesis_library_, StopSpeaking()) - .Times(1) .WillOnce(Return(true)) .RetiresOnSaturation(); - EXPECT_CALL(*mock_speech_synthesis_library_, IsSpeaking()) - .Times(4) + EXPECT_CALL(*mock_speech_synthesis_library_, Speak(_)) .WillOnce(Return(true)) + .RetiresOnSaturation(); + EXPECT_CALL(*mock_speech_synthesis_library_, IsSpeaking()) .WillOnce(Return(true)) .WillOnce(Return(true)) .WillOnce(Return(false)) diff --git a/chrome/browser/extensions/extension_tts_api.cc b/chrome/browser/extensions/extension_tts_api.cc index b022ec0..1654b071 100644 --- a/chrome/browser/extensions/extension_tts_api.cc +++ b/chrome/browser/extensions/extension_tts_api.cc @@ -7,68 +7,206 @@ #include <string> #include "base/float_util.h" +#include "base/message_loop.h" #include "base/values.h" namespace util = extension_tts_api_util; namespace { - const char kCrosLibraryNotLoadedError[] = - "Cros shared library not loaded."; +const char kCrosLibraryNotLoadedError[] = + "Cros shared library not loaded."; +const int kSpeechCheckDelayIntervalMs = 100; }; +// static +ExtensionTtsController* ExtensionTtsController::GetInstance() { + return Singleton<ExtensionTtsController>::get(); +} + +ExtensionTtsController::ExtensionTtsController() + : ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), + current_utterance_(NULL), + platform_impl_(NULL) { +} + +void ExtensionTtsController::SpeakOrEnqueue( + Utterance* utterance, bool can_enqueue) { + if (IsSpeaking() && can_enqueue) { + utterance_queue_.push(utterance); + } else { + Stop(); + SpeakNow(utterance); + } +} + +void ExtensionTtsController::SpeakNow(Utterance* utterance) { + GetPlatformImpl()->clear_error(); + bool success = GetPlatformImpl()->Speak( + utterance->text, + utterance->language, + utterance->gender, + utterance->rate, + utterance->pitch, + utterance->volume); + if (!success) { + utterance->error = GetPlatformImpl()->error(); + utterance->failure_task->Run(); + delete utterance->success_task; + delete utterance; + return; + } + current_utterance_ = utterance; + + // Post a task to check if this utterance has completed after a delay. + MessageLoop::current()->PostDelayedTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &ExtensionTtsController::CheckSpeechStatus), + kSpeechCheckDelayIntervalMs); +} + +void ExtensionTtsController::Stop() { + GetPlatformImpl()->clear_error(); + GetPlatformImpl()->StopSpeaking(); + + FinishCurrentUtterance(); + ClearUtteranceQueue(); +} + +bool ExtensionTtsController::IsSpeaking() const { + return current_utterance_ != NULL; +} + +void ExtensionTtsController::FinishCurrentUtterance() { + if (current_utterance_) { + current_utterance_->success_task->Run(); + delete current_utterance_->failure_task; + delete current_utterance_; + current_utterance_ = NULL; + } +} + +void ExtensionTtsController::ClearUtteranceQueue() { + while (!utterance_queue_.empty()) { + Utterance* utterance = utterance_queue_.front(); + utterance_queue_.pop(); + utterance->success_task->Run(); + delete utterance->failure_task; + delete utterance; + } +} + +void ExtensionTtsController::CheckSpeechStatus() { + if (!current_utterance_) + return; + + if (GetPlatformImpl()->IsSpeaking() == false) { + FinishCurrentUtterance(); + + // Start speaking the next utterance in the queue. Keep trying in case + // one fails but there are still more in the queue to try. + while (!utterance_queue_.empty() && !current_utterance_) { + Utterance* utterance = utterance_queue_.front(); + utterance_queue_.pop(); + SpeakNow(utterance); + } + } + + // If we're still speaking something (either the prevoius utterance or + // a new utterance), keep calling this method after another delay. + if (current_utterance_) { + MessageLoop::current()->PostDelayedTask( + FROM_HERE, method_factory_.NewRunnableMethod( + &ExtensionTtsController::CheckSpeechStatus), + kSpeechCheckDelayIntervalMs); + } +} + +void ExtensionTtsController::SetPlatformImpl( + ExtensionTtsPlatformImpl* platform_impl) { + platform_impl_ = platform_impl; +} + +ExtensionTtsPlatformImpl* ExtensionTtsController::GetPlatformImpl() { + if (!platform_impl_) + platform_impl_ = ExtensionTtsPlatformImpl::GetInstance(); + return platform_impl_; +} + +// +// Extension API functions +// + bool ExtensionTtsSpeakFunction::RunImpl() { - std::string utterance; - std::string language; - std::string gender; - double rate = -1.0; - double pitch = -1.0; - double volume = -1.0; + utterance_ = new ExtensionTtsController::Utterance(); + bool can_enqueue = false; DictionaryValue* speak_options = NULL; - EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance)); + EXTENSION_FUNCTION_VALIDATE(args_->GetString(0, &utterance_->text)); if (args_->GetDictionary(1, &speak_options)) { if (speak_options->HasKey(util::kLanguageNameKey)) { - speak_options->GetString(util::kLanguageNameKey, &language); + speak_options->GetString(util::kLanguageNameKey, &utterance_->language); } if (speak_options->HasKey(util::kGenderKey)) { - speak_options->GetString(util::kGenderKey, &gender); + speak_options->GetString(util::kGenderKey, &utterance_->gender); + } + + if (speak_options->HasKey(util::kEnqueueKey)) { + speak_options->GetBoolean(util::kEnqueueKey, &can_enqueue); } - if (util::ReadNumberByKey(speak_options, util::kRateKey, &rate)) { - if (!base::IsFinite(rate) || rate < 0.0 || rate > 1.0) { - rate = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kRateKey, &utterance_->rate)) { + if (!base::IsFinite(utterance_->rate) || + utterance_->rate < 0.0 || + utterance_->rate > 1.0) { + utterance_->rate = -1.0; } } - if (util::ReadNumberByKey(speak_options, util::kPitchKey, &pitch)) { - if (!base::IsFinite(pitch) || pitch < 0.0 || pitch > 1.0) { - pitch = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kPitchKey, &utterance_->pitch)) { + if (!base::IsFinite(utterance_->pitch) || + utterance_->pitch < 0.0 || + utterance_->pitch > 1.0) { + utterance_->pitch = -1.0; } } - if (util::ReadNumberByKey(speak_options, util::kVolumeKey, &volume)) { - if (!base::IsFinite(volume) || volume < 0.0 || volume > 1.0) { - volume = -1.0; + if (util::ReadNumberByKey( + speak_options, util::kVolumeKey, &utterance_->volume)) { + if (!base::IsFinite(utterance_->volume) || + utterance_->volume < 0.0 || + utterance_->volume > 1.0) { + utterance_->volume = -1.0; } } } - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - return impl->Speak(utterance, language, gender, rate, pitch, volume); + AddRef(); // Balanced in SpeechFinished(). + utterance_->success_task = NewRunnableMethod( + this, &ExtensionTtsSpeakFunction::SpeechFinished, true); + utterance_->failure_task = NewRunnableMethod( + this, &ExtensionTtsSpeakFunction::SpeechFinished, false); + ExtensionTtsController::GetInstance()->SpeakOrEnqueue( + utterance_, can_enqueue); + return true; +} + +void ExtensionTtsSpeakFunction::SpeechFinished(bool success) { + error_ = utterance_->error; + SendResponse(success); + Release(); // Balanced in Speak(). } bool ExtensionTtsStopSpeakingFunction::RunImpl() { - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - return impl->StopSpeaking(); + ExtensionTtsController::GetInstance()->Stop(); + return true; } bool ExtensionTtsIsSpeakingFunction::RunImpl() { - ExtensionTtsPlatformImpl* impl = ExtensionTtsPlatformImpl::GetInstance(); - impl->clear_error(); - result_.reset(Value::CreateBooleanValue(impl->IsSpeaking())); + result_.reset(Value::CreateBooleanValue( + ExtensionTtsController::GetInstance()->IsSpeaking())); return true; } diff --git a/chrome/browser/extensions/extension_tts_api.h b/chrome/browser/extensions/extension_tts_api.h index 73fc887..0eedcc2 100644 --- a/chrome/browser/extensions/extension_tts_api.h +++ b/chrome/browser/extensions/extension_tts_api.h @@ -5,6 +5,10 @@ #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_TTS_API_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_TTS_API_H_ +#include <queue> + +#include "base/singleton.h" +#include "base/task.h" #include "chrome/browser/extensions/extension_function.h" #include "chrome/browser/extensions/extension_tts_api_util.h" @@ -17,6 +21,11 @@ class ExtensionTtsPlatformImpl { // and return true on success. Utterance will always be nonempty. // If the user does not specify the other values, language and gender // will be empty strings, and rate, pitch, and volume will be -1.0. + // + // The ExtensionTtsController will only try to speak one utterance at + // a time. If it wants to intterupt speech, it will always call Stop + // before speaking again, otherwise it will wait until IsSpeaking + // returns false before calling Speak again. virtual bool Speak( const std::string& utterance, const std::string& language, @@ -44,23 +53,108 @@ class ExtensionTtsPlatformImpl { DISALLOW_COPY_AND_ASSIGN(ExtensionTtsPlatformImpl); }; +// Singleton class that manages text-to-speech. +class ExtensionTtsController { + public: + // Get the single instance of this class. + static ExtensionTtsController* GetInstance(); + + struct Utterance { + Utterance() + : rate(-1.0), + pitch(-1.0), + volume(-1.0), + success_task(NULL), + failure_task(NULL) { + } + + std::string text; + std::string language; + std::string gender; + double rate; + double pitch; + double volume; + + Task* success_task; + Task* failure_task; + + std::string error; + }; + + // Returns true if we're currently speaking an utterance. + bool IsSpeaking() const; + + // Speak the given utterance. If |can_enqueue| is true and another + // utterance is in progress, adds it to the end of the queue. Otherwise, + // interrupts any current utterance and speaks this one immediately. + void SpeakOrEnqueue(Utterance* utterance, bool can_enqueue); + + // Stop all utterances and flush the queue. + void Stop(); + + // For unit testing. + void SetPlatformImpl(ExtensionTtsPlatformImpl* platform_impl); + + private: + ExtensionTtsController(); + virtual ~ExtensionTtsController() {} + + // Get the platform TTS implementation (or injected mock). + ExtensionTtsPlatformImpl* GetPlatformImpl(); + + // Start speaking the given utterance. Will either take ownership of + // |utterance| or delete it if there's an error. + void SpeakNow(Utterance* utterance); + + // Called periodically when speech is ongoing. Checks to see if the + // underlying platform speech system has finished the current utterance, + // and if so finishes it and pops the next utterance off the queue. + void CheckSpeechStatus(); + + // Clear the utterance queue. + void ClearUtteranceQueue(); + + // Finalize and delete the current utterance. + void FinishCurrentUtterance(); + + ScopedRunnableMethodFactory<ExtensionTtsController> method_factory_; + friend struct DefaultSingletonTraits<ExtensionTtsController>; + + // The current utterance being spoken. + Utterance* current_utterance_; + + // A queue of utterances to speak after the current one finishes. + std::queue<Utterance*> utterance_queue_; + + // A pointer to the platform implementation of text-to-speech, for + // dependency injection. + ExtensionTtsPlatformImpl* platform_impl_; + + DISALLOW_COPY_AND_ASSIGN(ExtensionTtsController); +}; + // // Extension API function definitions // -class ExtensionTtsSpeakFunction : public SyncExtensionFunction { +class ExtensionTtsSpeakFunction : public AsyncExtensionFunction { + private: ~ExtensionTtsSpeakFunction() {} virtual bool RunImpl(); + void SpeechFinished(bool success); + ExtensionTtsController::Utterance* utterance_; DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.speak") }; class ExtensionTtsStopSpeakingFunction : public SyncExtensionFunction { + private: ~ExtensionTtsStopSpeakingFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.stop") }; class ExtensionTtsIsSpeakingFunction : public SyncExtensionFunction { + private: ~ExtensionTtsIsSpeakingFunction() {} virtual bool RunImpl(); DECLARE_EXTENSION_FUNCTION_NAME("experimental.tts.isSpeaking") diff --git a/chrome/browser/extensions/extension_tts_api_linux.cc b/chrome/browser/extensions/extension_tts_api_linux.cc index 8ee228b..7b89799 100644 --- a/chrome/browser/extensions/extension_tts_api_linux.cc +++ b/chrome/browser/extensions/extension_tts_api_linux.cc @@ -38,7 +38,7 @@ class ExtensionTtsPlatformImplLinux : public ExtensionTtsPlatformImpl { // Get the single instance of this class. static ExtensionTtsPlatformImplLinux* GetInstance() { - return ExtensionTtsPlatformImplLinux::GetInstance(); + return Singleton<ExtensionTtsPlatformImplLinux>::get(); } private: diff --git a/chrome/browser/extensions/extension_tts_api_util.h b/chrome/browser/extensions/extension_tts_api_util.h index 4b41871..cf0d702 100644 --- a/chrome/browser/extensions/extension_tts_api_util.h +++ b/chrome/browser/extensions/extension_tts_api_util.h @@ -17,6 +17,7 @@ const char kGenderKey[] = "gender"; const char kRateKey[] = "rate"; const char kPitchKey[] = "pitch"; const char kVolumeKey[] = "volume"; +const char kEnqueueKey[] = "enqueue"; const char kEqualStr[] = "="; const char kDelimiter[] = ";"; diff --git a/chrome/browser/extensions/extension_tts_apitest.cc b/chrome/browser/extensions/extension_tts_apitest.cc index dcdfb9d5..6f37404 100644 --- a/chrome/browser/extensions/extension_tts_apitest.cc +++ b/chrome/browser/extensions/extension_tts_apitest.cc @@ -2,19 +2,164 @@ // 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/chromeos/cros/cros_mock.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" -// This extension API is currently only supported on Chrome OS. #if defined(OS_CHROMEOS) -#define MAYBE_Tts Tts -#else -#define MAYBE_Tts DISABLED_Tts +#include "chrome/browser/chromeos/cros/cros_mock.h" #endif -IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tts) { +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& language, + 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) { + 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_, 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)); + 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_, Speak(_, _, _, _, _, _)) + .WillOnce(DoAll( + InvokeWithoutArgs( + CreateFunctor(&mock_platform_impl_, + &MockExtensionTtsPlatformImpl::SetErrorToEpicFail)), + Return(false))); + 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_CHROMEOS) +IN_PROC_BROWSER_TEST_F(ExtensionApiTest, TtsChromeOs) { CommandLine::ForCurrentProcess()->AppendSwitch( switches::kEnableExperimentalExtensionApis); @@ -24,3 +169,4 @@ IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_Tts) { ASSERT_TRUE(RunExtensionTest("tts/chromeos")) << message_; } +#endif diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 61bf901..7bf15f7 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -2031,10 +2031,6 @@ ['chromeos==0', { 'sources/': [ ['exclude', '^browser/chromeos'], - # Currently TTS extension API is supported only for chromeos. - # Remove the following exclude when support for other platforms - # is added. - ['exclude', 'browser/extensions/extension_tts_apitest.cc'], ], 'sources!': [ 'browser/dom_ui/mediaplayer_browsertest.cc', diff --git a/chrome/common/extensions/api/extension_api.json b/chrome/common/extensions/api/extension_api.json index 3fdd1ce..5936a3e 100644 --- a/chrome/common/extensions/api/extension_api.json +++ b/chrome/common/extensions/api/extension_api.json @@ -503,7 +503,12 @@ "optional": true, "description": "The speak options. This parameter is currently ignored.", "properties": { - "languageName": { + "enqueue": { + "type": "boolean", + "optional": true, + "description": "If true, enqueues this utterance if TTS is already in progress. If false (the default), interrupts any current speech and flushes the speech queue before speaking this new utterance." + }, + "languageName": { "type": "string", "optional": true, "description": "The language name for synthesis specified in the form <language>-<locale>, e.g. en-US, en-GB, fr-CA, zh-CN, etc." diff --git a/chrome/test/data/extensions/api_test/tts/chromeos/test.js b/chrome/test/data/extensions/api_test/tts/chromeos/test.js index 0cf73bc0..9db8d96 100644 --- a/chrome/test/data/extensions/api_test/tts/chromeos/test.js +++ b/chrome/test/data/extensions/api_test/tts/chromeos/test.js @@ -3,29 +3,23 @@ // found in the LICENSE file. // TTS api test for Chrome on ChromeOS. -// browser_tests.exe --gtest_filter=ExtensionApiTest.TtsOnChromeOs +// browser_tests.exe --gtest_filter="ExtensionApiTest.TtsChromeOs" chrome.test.runTests([ - function testSpeak() { - chrome.experimental.tts.speak('hello world', {}, function() { + function testChromeOsSpeech() { + var callbacks = 0; + chrome.experimental.tts.speak('text 1', {}, function() { + chrome.test.assertNoLastError(); + callbacks++; + }); + chrome.experimental.tts.speak('text 2', {}, function() { + chrome.test.assertNoLastError(); + callbacks++; + if (callbacks == 2) { chrome.test.succeed(); - }); - }, - - function testStop() { - chrome.experimental.tts.stop(); - chrome.test.succeed(); - }, - - function testIsSpeaking() { - for (var i = 0; i < 3; i++) { - chrome.experimental.tts.isSpeaking(function(speaking) { - chrome.test.assertTrue(speaking); - }); - } - chrome.experimental.tts.isSpeaking(function(speaking) { - chrome.test.assertFalse(speaking); - chrome.test.succeed(); + } else { + chrome.test.fail(); + } }); } diff --git a/chrome/test/data/extensions/api_test/tts/enqueue/manifest.json b/chrome/test/data/extensions/api_test/tts/enqueue/manifest.json new file mode 100644 index 0000000..a43bc3d --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/enqueue/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.experimental.tts", + "version": "0.1", + "description": "browser test for chrome.experimental.tts API", + "background_page": "test.html", + "permissions": ["experimental"] +} diff --git a/chrome/test/data/extensions/api_test/tts/enqueue/test.html b/chrome/test/data/extensions/api_test/tts/enqueue/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/enqueue/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/tts/enqueue/test.js b/chrome/test/data/extensions/api_test/tts/enqueue/test.js new file mode 100644 index 0000000..a903ab3 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/enqueue/test.js @@ -0,0 +1,25 @@ +// 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. + +// TTS api test for Chrome on ChromeOS. +// browser_tests.exe --gtest_filter="TtsApiTest.*" + +chrome.test.runTests([ + function testAllSpeakCallbackFunctionsAreCalled() { + var callbacks = 0; + chrome.experimental.tts.speak('text 1', {'enqueue': true}, function() { + chrome.test.assertNoLastError(); + callbacks++; + }); + chrome.experimental.tts.speak('text 2', {'enqueue': true}, function() { + chrome.test.assertNoLastError(); + callbacks++; + if (callbacks == 2) { + chrome.test.succeed(); + } else { + chrome.test.fail(); + } + }); + } +]); diff --git a/chrome/test/data/extensions/api_test/tts/interrupt/manifest.json b/chrome/test/data/extensions/api_test/tts/interrupt/manifest.json new file mode 100644 index 0000000..a43bc3d --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/interrupt/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.experimental.tts", + "version": "0.1", + "description": "browser test for chrome.experimental.tts API", + "background_page": "test.html", + "permissions": ["experimental"] +} diff --git a/chrome/test/data/extensions/api_test/tts/interrupt/test.html b/chrome/test/data/extensions/api_test/tts/interrupt/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/interrupt/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/tts/interrupt/test.js b/chrome/test/data/extensions/api_test/tts/interrupt/test.js new file mode 100644 index 0000000..51759b2 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/interrupt/test.js @@ -0,0 +1,25 @@ +// 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. + +// TTS api test for Chrome on ChromeOS. +// browser_tests.exe --gtest_filter="TtsApiTest.*" + +chrome.test.runTests([ + function testAllSpeakCallbackFunctionsAreCalled() { + var callbacks = 0; + chrome.experimental.tts.speak('text 1', {'enqueue': false}, function() { + chrome.test.assertNoLastError(); + callbacks++; + }); + chrome.experimental.tts.speak('text 2', {'enqueue': false}, function() { + chrome.test.assertNoLastError(); + callbacks++; + if (callbacks == 2) { + chrome.test.succeed(); + } else { + chrome.test.fail(); + } + }); + } +]); diff --git a/chrome/test/data/extensions/api_test/tts/queue_interrupt/manifest.json b/chrome/test/data/extensions/api_test/tts/queue_interrupt/manifest.json new file mode 100644 index 0000000..a43bc3d --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/queue_interrupt/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.experimental.tts", + "version": "0.1", + "description": "browser test for chrome.experimental.tts API", + "background_page": "test.html", + "permissions": ["experimental"] +} diff --git a/chrome/test/data/extensions/api_test/tts/queue_interrupt/test.html b/chrome/test/data/extensions/api_test/tts/queue_interrupt/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/queue_interrupt/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/tts/queue_interrupt/test.js b/chrome/test/data/extensions/api_test/tts/queue_interrupt/test.js new file mode 100644 index 0000000..c8137cf --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/queue_interrupt/test.js @@ -0,0 +1,29 @@ +// 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. + +// TTS api test for Chrome on ChromeOS. +// browser_tests.exe --gtest_filter="TtsApiTest.*" + +chrome.test.runTests([ + function testAllSpeakCallbackFunctionsAreCalled() { + var callbacks = 0; + chrome.experimental.tts.speak('text 1', {'enqueue': true}, function() { + chrome.test.assertNoLastError(); + callbacks++; + }); + chrome.experimental.tts.speak('text 2', {'enqueue': true}, function() { + chrome.test.assertNoLastError(); + callbacks++; + }); + chrome.experimental.tts.speak('text 3', {'enqueue': false}, function() { + chrome.test.assertNoLastError(); + callbacks++; + if (callbacks == 3) { + chrome.test.succeed(); + } else { + chrome.test.fail(); + } + }); + } +]); diff --git a/chrome/test/data/extensions/api_test/tts/speak_error/manifest.json b/chrome/test/data/extensions/api_test/tts/speak_error/manifest.json new file mode 100644 index 0000000..a43bc3d --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/speak_error/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.experimental.tts", + "version": "0.1", + "description": "browser test for chrome.experimental.tts API", + "background_page": "test.html", + "permissions": ["experimental"] +} diff --git a/chrome/test/data/extensions/api_test/tts/speak_error/test.html b/chrome/test/data/extensions/api_test/tts/speak_error/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/speak_error/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/tts/speak_error/test.js b/chrome/test/data/extensions/api_test/tts/speak_error/test.js new file mode 100644 index 0000000..637ee46 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/speak_error/test.js @@ -0,0 +1,29 @@ +// 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. + +// TTS api test for Chrome on ChromeOS. +// browser_tests.exe --gtest_filter="TtsApiTest.*" + +chrome.test.runTests([ + function testSpeakCallbackFunctionIsCalled() { + var callbacks = 0; + chrome.experimental.tts.speak('first try', {'enqueue': true}, function() { + chrome.test.assertNoLastError(); + callbacks++; + }); + chrome.experimental.tts.speak('second try', {'enqueue': true}, function() { + chrome.test.assertEq('epic fail', chrome.extension.lastError.message); + callbacks++; + }); + chrome.experimental.tts.speak('third try', {'enqueue': true}, function() { + chrome.test.assertNoLastError(); + callbacks++; + if (callbacks == 3) { + chrome.test.succeed(); + } else { + chrome.test.fail(); + } + }); + } +]); diff --git a/chrome/test/data/extensions/api_test/tts/speak_once/manifest.json b/chrome/test/data/extensions/api_test/tts/speak_once/manifest.json new file mode 100644 index 0000000..a43bc3d --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/speak_once/manifest.json @@ -0,0 +1,7 @@ +{ + "name": "chrome.experimental.tts", + "version": "0.1", + "description": "browser test for chrome.experimental.tts API", + "background_page": "test.html", + "permissions": ["experimental"] +} diff --git a/chrome/test/data/extensions/api_test/tts/speak_once/test.html b/chrome/test/data/extensions/api_test/tts/speak_once/test.html new file mode 100644 index 0000000..46f4d74 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/speak_once/test.html @@ -0,0 +1 @@ +<script src="test.js"></script> diff --git a/chrome/test/data/extensions/api_test/tts/speak_once/test.js b/chrome/test/data/extensions/api_test/tts/speak_once/test.js new file mode 100644 index 0000000..33fc8f5 --- /dev/null +++ b/chrome/test/data/extensions/api_test/tts/speak_once/test.js @@ -0,0 +1,15 @@ +// 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. + +// TTS api test for Chrome on ChromeOS. +// browser_tests.exe --gtest_filter="TtsApiTest.*" + +chrome.test.runTests([ + function testSpeakCallbackFunctionIsCalled() { + chrome.experimental.tts.speak('hello world', {}, function() { + chrome.test.assertNoLastError(); + chrome.test.succeed(); + }); + } +]); |