Home | History | Annotate | Download | only in speech
      1 // Copyright 2013 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 <list>
      6 
      7 #include "base/bind.h"
      8 #include "base/memory/scoped_ptr.h"
      9 #include "base/run_loop.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "content/browser/speech/google_streaming_remote_engine.h"
     12 #include "content/browser/speech/speech_recognition_manager_impl.h"
     13 #include "content/browser/speech/speech_recognizer_impl.h"
     14 #include "content/public/browser/browser_thread.h"
     15 #include "content/public/browser/notification_types.h"
     16 #include "content/public/browser/web_contents.h"
     17 #include "content/public/test/browser_test_utils.h"
     18 #include "content/public/test/content_browser_test.h"
     19 #include "content/public/test/content_browser_test_utils.h"
     20 #include "content/public/test/test_utils.h"
     21 #include "content/shell/browser/shell.h"
     22 #include "content/test/mock_google_streaming_server.h"
     23 #include "media/audio/mock_audio_manager.h"
     24 #include "media/audio/test_audio_input_controller_factory.h"
     25 #include "testing/gtest/include/gtest/gtest.h"
     26 
     27 using base::RunLoop;
     28 
     29 namespace content {
     30 
     31 class SpeechRecognitionBrowserTest :
     32     public ContentBrowserTest,
     33     public MockGoogleStreamingServer::Delegate,
     34     public media::TestAudioInputControllerDelegate {
     35  public:
     36   enum StreamingServerState {
     37     kIdle,
     38     kTestAudioControllerOpened,
     39     kClientConnected,
     40     kClientAudioUpload,
     41     kClientAudioUploadComplete,
     42     kTestAudioControllerClosed,
     43     kClientDisconnected
     44   };
     45 
     46   // MockGoogleStreamingServerDelegate methods.
     47   virtual void OnClientConnected() OVERRIDE {
     48     ASSERT_EQ(kTestAudioControllerOpened, streaming_server_state_);
     49     streaming_server_state_ = kClientConnected;
     50   }
     51 
     52   virtual void OnClientAudioUpload() OVERRIDE {
     53     if (streaming_server_state_ == kClientConnected)
     54       streaming_server_state_ = kClientAudioUpload;
     55   }
     56 
     57   virtual void OnClientAudioUploadComplete() OVERRIDE {
     58     ASSERT_EQ(kTestAudioControllerClosed, streaming_server_state_);
     59     streaming_server_state_ = kClientAudioUploadComplete;
     60   }
     61 
     62   virtual void OnClientDisconnected() OVERRIDE {
     63     ASSERT_EQ(kClientAudioUploadComplete, streaming_server_state_);
     64     streaming_server_state_ = kClientDisconnected;
     65   }
     66 
     67   // media::TestAudioInputControllerDelegate methods.
     68   virtual void TestAudioControllerOpened(
     69       media::TestAudioInputController* controller) OVERRIDE {
     70     ASSERT_EQ(kIdle, streaming_server_state_);
     71     streaming_server_state_ = kTestAudioControllerOpened;
     72     const int capture_packet_interval_ms =
     73         (1000 * controller->audio_parameters().frames_per_buffer()) /
     74         controller->audio_parameters().sample_rate();
     75     ASSERT_EQ(GoogleStreamingRemoteEngine::kAudioPacketIntervalMs,
     76         capture_packet_interval_ms);
     77     FeedAudioController(500 /* ms */, /*noise=*/ false);
     78     FeedAudioController(1000 /* ms */, /*noise=*/ true);
     79     FeedAudioController(1000 /* ms */, /*noise=*/ false);
     80   }
     81 
     82   virtual void TestAudioControllerClosed(
     83       media::TestAudioInputController* controller) OVERRIDE {
     84     ASSERT_EQ(kClientAudioUpload, streaming_server_state_);
     85     streaming_server_state_ = kTestAudioControllerClosed;
     86     mock_streaming_server_->MockGoogleStreamingServer::SimulateResult(
     87         GetGoodSpeechResult());
     88   }
     89 
     90   // Helper methods used by test fixtures.
     91   GURL GetTestUrlFromFragment(const std::string fragment) {
     92     return GURL(GetTestUrl("speech", "web_speech_recognition.html").spec() +
     93         "#" + fragment);
     94   }
     95 
     96   std::string GetPageFragment() {
     97     return shell()->web_contents()->GetURL().ref();
     98   }
     99 
    100   const StreamingServerState &streaming_server_state() {
    101     return streaming_server_state_;
    102   }
    103 
    104  protected:
    105   // ContentBrowserTest methods.
    106   virtual void SetUpInProcessBrowserTestFixture() OVERRIDE {
    107     test_audio_input_controller_factory_.set_delegate(this);
    108     media::AudioInputController::set_factory_for_testing(
    109         &test_audio_input_controller_factory_);
    110     mock_streaming_server_.reset(new MockGoogleStreamingServer(this));
    111     streaming_server_state_ = kIdle;
    112   }
    113 
    114   virtual void SetUpOnMainThread() OVERRIDE {
    115     ASSERT_TRUE(SpeechRecognitionManagerImpl::GetInstance());
    116     SpeechRecognizerImpl::SetAudioManagerForTesting(
    117         new media::MockAudioManager(BrowserThread::GetMessageLoopProxyForThread(
    118             BrowserThread::IO)));
    119   }
    120 
    121   virtual void TearDownOnMainThread() OVERRIDE {
    122     SpeechRecognizerImpl::SetAudioManagerForTesting(NULL);
    123   }
    124 
    125   virtual void TearDownInProcessBrowserTestFixture() OVERRIDE {
    126     test_audio_input_controller_factory_.set_delegate(NULL);
    127     mock_streaming_server_.reset();
    128   }
    129 
    130  private:
    131   static void FeedSingleBufferToAudioController(
    132       scoped_refptr<media::TestAudioInputController> controller,
    133       size_t buffer_size,
    134       bool fill_with_noise) {
    135     DCHECK(controller.get());
    136     const media::AudioParameters& audio_params = controller->audio_parameters();
    137     scoped_ptr<uint8[]> audio_buffer(new uint8[buffer_size]);
    138     if (fill_with_noise) {
    139       for (size_t i = 0; i < buffer_size; ++i)
    140         audio_buffer[i] = static_cast<uint8>(127 * sin(i * 3.14F /
    141             (16 * buffer_size)));
    142     } else {
    143       memset(audio_buffer.get(), 0, buffer_size);
    144     }
    145 
    146     scoped_ptr<media::AudioBus> audio_bus =
    147         media::AudioBus::Create(audio_params);
    148     audio_bus->FromInterleaved(&audio_buffer.get()[0],
    149                                audio_bus->frames(),
    150                                audio_params.bits_per_sample() / 8);
    151     controller->event_handler()->OnData(controller, audio_bus.get());
    152   }
    153 
    154   void FeedAudioController(int duration_ms, bool feed_with_noise) {
    155     media::TestAudioInputController* controller =
    156         test_audio_input_controller_factory_.controller();
    157     ASSERT_TRUE(controller);
    158     const media::AudioParameters& audio_params = controller->audio_parameters();
    159     const size_t buffer_size = audio_params.GetBytesPerBuffer();
    160     const int ms_per_buffer = audio_params.frames_per_buffer() * 1000 /
    161                               audio_params.sample_rate();
    162     // We can only simulate durations that are integer multiples of the
    163     // buffer size. In this regard see
    164     // SpeechRecognitionEngine::GetDesiredAudioChunkDurationMs().
    165     ASSERT_EQ(0, duration_ms % ms_per_buffer);
    166 
    167     const int n_buffers = duration_ms / ms_per_buffer;
    168     for (int i = 0; i < n_buffers; ++i) {
    169       base::MessageLoop::current()->PostTask(FROM_HERE, base::Bind(
    170           &FeedSingleBufferToAudioController,
    171           scoped_refptr<media::TestAudioInputController>(controller),
    172           buffer_size,
    173           feed_with_noise));
    174     }
    175   }
    176 
    177   SpeechRecognitionResult GetGoodSpeechResult() {
    178     SpeechRecognitionResult result;
    179     result.hypotheses.push_back(SpeechRecognitionHypothesis(
    180         base::UTF8ToUTF16("Pictures of the moon"), 1.0F));
    181     return result;
    182   }
    183 
    184   StreamingServerState streaming_server_state_;
    185   scoped_ptr<MockGoogleStreamingServer> mock_streaming_server_;
    186   media::TestAudioInputControllerFactory test_audio_input_controller_factory_;
    187 };
    188 
    189 // Simply loads the test page and checks if it was able to create a Speech
    190 // Recognition object in JavaScript, to make sure the Web Speech API is enabled.
    191 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, Precheck) {
    192   NavigateToURLBlockUntilNavigationsComplete(
    193       shell(), GetTestUrlFromFragment("precheck"), 2);
    194 
    195   EXPECT_EQ(kIdle, streaming_server_state());
    196   EXPECT_EQ("success", GetPageFragment());
    197 }
    198 
    199 IN_PROC_BROWSER_TEST_F(SpeechRecognitionBrowserTest, OneShotRecognition) {
    200   NavigateToURLBlockUntilNavigationsComplete(
    201       shell(), GetTestUrlFromFragment("oneshot"), 2);
    202 
    203   EXPECT_EQ(kClientDisconnected, streaming_server_state());
    204   EXPECT_EQ("goodresult1", GetPageFragment());
    205 }
    206 
    207 }  // namespace content
    208