910e62b5创建于 1月15日历史提交
// Copyright 2020 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromeos/services/tts/tts_service.h"

#include "chromeos/services/tts/public/mojom/tts_service.mojom.h"
#include "chromeos/services/tts/tts_test_utils.h"
#include "media/base/audio_bus.h"

namespace chromeos {
namespace tts {
namespace {

// Tests the TtsService interface, the main interface that handles TTS
// requests from clients on ChromeOS. For more info, please see
// chromeos/services/tts/public/mojom/tts_service.mojom.
class TtsServiceTest : public TtsTestBase {
 public:
  TtsServiceTest() : service_(remote_service_.BindNewPipeAndPassReceiver()) {}
  TtsServiceTest(const TtsServiceTest&) = delete;
  TtsServiceTest& operator=(const TtsServiceTest&) = delete;
  ~TtsServiceTest() override = default;

 protected:
  void InitPlaybackTtsStream(mojo::Remote<mojom::PlaybackTtsStream>* stream) {
    // Audio stream factory is here to get a basic environment working only.
    // Unbind and rebind if needed.
    if (audio_stream_factory_.is_bound())
      audio_stream_factory_.reset();

    auto callback = base::BindOnce([](mojom::AudioParametersPtr) {
      // Do nothing.
    });

    mojom::AudioParametersPtr desired_audio_parameters =
        mojom::AudioParameters::New(20000 /* sample rate */,
                                    128 /* buffer size */);
    remote_service_->BindPlaybackTtsStream(
        stream->BindNewPipeAndPassReceiver(),
        audio_stream_factory_.BindNewPipeAndPassRemote(),
        std::move(desired_audio_parameters), std::move(callback));
    remote_service_.FlushForTesting();
  }

  // testing::Test:
  void SetUp() override { service_.set_keep_process_alive_for_testing(true); }

  mojo::Remote<mojom::TtsService> remote_service_;
  TtsService service_;
};

TEST_F(TtsServiceTest, DisconnectPlaybackStream) {
  // Create the first tts stream factory and request a playback stream.
  mojo::Remote<mojom::PlaybackTtsStream> stream1;
  InitPlaybackTtsStream(&stream1);

  // There's an active playback stream, so the tts service receiver should still
  // be bound.
  EXPECT_TRUE(service_.receiver_for_testing()->is_bound());

  // Simulate disconnecting the remote here (e.g. extension closes).
  stream1.reset();
  service_.playback_tts_stream_for_testing()->FlushForTesting();

  // The tts service receiver should have been reset, indicating the
  // process would have been exited in production.
  EXPECT_FALSE(service_.receiver_for_testing()->is_bound());
}

TEST_F(TtsServiceTest, BasicAudioBuffering) {
  mojo::Remote<mojom::PlaybackTtsStream> playback_tts_stream;
  InitPlaybackTtsStream(&playback_tts_stream);

  MockTtsEventObserver backing_observer;
  mojo::Receiver<mojom::TtsEventObserver> observer(&backing_observer);
  playback_tts_stream->Play(base::BindOnce(
      [](mojo::Receiver<mojom::TtsEventObserver>* receiver,
         mojo::PendingReceiver<mojom::TtsEventObserver> pending_receiver) {
        receiver->Bind(std::move(pending_receiver));
      },
      &observer));
  playback_tts_stream.FlushForTesting();
  service_.playback_tts_stream_for_testing()->FlushForTesting();

  auto bus = media::AudioBus::Create(1 /* channels */, 512 /* frames */);
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();

  // The playback stream pushes an empty buffer to trigger a start event.
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_TRUE(backing_observer.char_indices.empty());
  EXPECT_EQ(0, backing_observer.end_count);

  playback_tts_stream->SendAudioBuffer(
      std::vector<float>(), 100 /* char_index */, false /* last buffer */);
  playback_tts_stream.FlushForTesting();
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_EQ(1U, backing_observer.char_indices.size());
  EXPECT_EQ(100, backing_observer.char_indices[0]);
  EXPECT_EQ(0, backing_observer.end_count);

  // Note that the cahr index is ignored for the end of all audio as it's
  // assumed to be the length of the utterance.
  playback_tts_stream->SendAudioBuffer(
      std::vector<float>(), 9999 /* char_index */, true /* last buffer */);
  playback_tts_stream.FlushForTesting();
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_EQ(1U, backing_observer.char_indices.size());
  EXPECT_EQ(1, backing_observer.end_count);
}

TEST_F(TtsServiceTest, ExplicitAudioTimepointing) {
  mojo::Remote<mojom::PlaybackTtsStream> playback_tts_stream;
  InitPlaybackTtsStream(&playback_tts_stream);

  MockTtsEventObserver backing_observer;
  mojo::Receiver<mojom::TtsEventObserver> observer(&backing_observer);
  playback_tts_stream->Play(base::BindOnce(
      [](mojo::Receiver<mojom::TtsEventObserver>* receiver,
         mojo::PendingReceiver<mojom::TtsEventObserver> pending_receiver) {
        receiver->Bind(std::move(pending_receiver));
      },
      &observer));
  playback_tts_stream.FlushForTesting();

  auto bus = media::AudioBus::Create(1 /* channels */, 512 /* frames */);
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();

  // The playback stream pushes an empty buffer to trigger a start event.
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_TRUE(backing_observer.char_indices.empty());
  EXPECT_EQ(0, backing_observer.end_count);

  playback_tts_stream->SendAudioBuffer(
      std::vector<float>(), -1 /* char_index */, false /* last buffer */);
  playback_tts_stream.FlushForTesting();
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_TRUE(backing_observer.char_indices.empty());
  EXPECT_EQ(0, backing_observer.end_count);

  playback_tts_stream->SendAudioBuffer(
      std::vector<float>(), -1 /* char_index */, false /* last buffer */);
  service_.playback_tts_stream_for_testing()
      ->tts_player_for_testing()
      ->AddExplicitTimepoint(100, base::Seconds(0));
  service_.playback_tts_stream_for_testing()
      ->tts_player_for_testing()
      ->AddExplicitTimepoint(200, base::Seconds(0));
  service_.playback_tts_stream_for_testing()
      ->tts_player_for_testing()
      ->AddExplicitTimepoint(300, base::Seconds(0));
  playback_tts_stream.FlushForTesting();
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_EQ(3U, backing_observer.char_indices.size());
  EXPECT_EQ(100, backing_observer.char_indices[0]);
  EXPECT_EQ(200, backing_observer.char_indices[1]);
  EXPECT_EQ(300, backing_observer.char_indices[2]);
  EXPECT_EQ(0, backing_observer.end_count);

  playback_tts_stream->SendAudioBuffer(
      std::vector<float>(), 9999 /* char_index */, true /* last buffer */);
  playback_tts_stream.FlushForTesting();
  service_.playback_tts_stream_for_testing()->tts_player_for_testing()->Render(
      base::Seconds(0), base::TimeTicks::Now(), {} /* glitch info */,
      bus.get());
  observer.FlushForTesting();
  EXPECT_EQ(1, backing_observer.start_count);
  EXPECT_EQ(3U, backing_observer.char_indices.size());
  EXPECT_EQ(1, backing_observer.end_count);
}

}  // namespace
}  // namespace tts
}  // namespace chromeos