chromium/chromecast/media/audio/cast_audio_manager_unittest.cc

// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chromecast/media/audio/cast_audio_manager.h"

#include <memory>
#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/test/task_environment.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "chromecast/chromecast_buildflags.h"
#include "chromecast/media/api/cma_backend.h"
#include "chromecast/media/api/test/mock_cma_backend.h"
#include "chromecast/media/api/test/mock_cma_backend_factory.h"
#include "chromecast/media/audio/mock_cast_audio_manager_helper_delegate.h"
#include "media/audio/audio_device_info_accessor_for_tests.h"
#include "media/audio/fake_audio_log_factory.h"
#include "media/audio/mock_audio_source_callback.h"
#include "media/audio/test_audio_thread.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#if BUILDFLAG(IS_ANDROID)
#include "media/audio/android/audio_track_output_stream.h"
#endif  // BUILDFLAG(IS_ANDROID)

using testing::_;
using testing::AnyNumber;
using testing::Invoke;
using testing::NiceMock;
using testing::Return;
using testing::StrictMock;

namespace {

const ::media::AudioParameters kDefaultAudioParams(
    ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
    ::media::ChannelLayoutConfig::Stereo(),
    ::media::AudioParameters::kAudioCDSampleRate,
    256);

const ::media::AudioParameters kAudioParamsInvalidLayout(
    ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
    ::media::ChannelLayoutConfig::FromLayout<::media::CHANNEL_LAYOUT_NONE>(),
    ::media::AudioParameters::kAudioCDSampleRate,
    256);

int OnMoreData(base::TimeDelta delay,
               base::TimeTicks delay_timestamp,
               const ::media::AudioGlitchInfo& glitch_info,
               ::media::AudioBus* dest) {
  dest->Zero();
  return kDefaultAudioParams.frames_per_buffer();
}

}  // namespace

namespace chromecast {
namespace media {

class CastAudioManagerTest : public testing::Test {
 public:
  CastAudioManagerTest() : audio_thread_("CastAudioThread") {}

  void SetUp() override { CreateAudioManagerForTesting(); }

  void TearDown() override {
    RunThreadsUntilIdle();
    audio_manager_->Shutdown();
    RunThreadsUntilIdle();
    audio_thread_.Stop();
  }

 protected:
  void FakeLogCallback(const std::string& string) {}
  CmaBackendFactory* GetCmaBackendFactory() {
    return mock_backend_factory_.get();
  }

  void CreateAudioManagerForTesting(bool use_mixer = false) {
    // Only one AudioManager may exist at a time, so destroy the one we're
    // currently holding before creating a new one.
    // Flush the message loop to run any shutdown tasks posted by AudioManager.
    if (audio_manager_) {
      audio_manager_->Shutdown();
      audio_manager_.reset();
    }
    if (audio_thread_.IsRunning()) {
      audio_thread_.Stop();
    }
    CHECK(audio_thread_.StartAndWaitForTesting());

    mock_backend_factory_ = std::make_unique<MockCmaBackendFactory>();
    audio_manager_ = base::WrapUnique(new CastAudioManager(
        std::make_unique<::media::TestAudioThread>(), &fake_audio_log_factory_,
        &mock_delegate_,
        base::BindRepeating(&CastAudioManagerTest::GetCmaBackendFactory,
                            base::Unretained(this)),
        task_environment_.GetMainThreadTaskRunner(),
        audio_thread_.task_runner(), use_mixer,
        true /* force_use_cma_backend_for_output*/
        ));
    // A few AudioManager implementations post initialization tasks to
    // audio thread. Flush the thread to ensure that |audio_manager_| is
    // initialized and ready to use before returning from this function.
    // TODO(alokp): We should perhaps do this in AudioManager::Create().
    RunThreadsUntilIdle();
    device_info_accessor_ =
        std::make_unique<::media::AudioDeviceInfoAccessorForTests>(
            audio_manager_.get());
  }

  void SetUpBackendAndDecoder() {
    mock_audio_decoder_ =
        std::make_unique<NiceMock<MockCmaBackend::AudioDecoder>>();
    EXPECT_CALL(*mock_audio_decoder_, SetDelegate(_)).Times(1);
    EXPECT_CALL(*mock_audio_decoder_, SetConfig(_)).WillOnce(Return(true));

    mock_cma_backend_ = std::make_unique<NiceMock<MockCmaBackend>>();
    EXPECT_CALL(*mock_cma_backend_, CreateAudioDecoder())
        .WillOnce(Return(mock_audio_decoder_.get()));
    EXPECT_CALL(*mock_cma_backend_, Initialize()).WillOnce(Return(true));

    EXPECT_CALL(*mock_backend_factory_, CreateBackend(_))
        .WillOnce(Invoke([this](const MediaPipelineDeviceParams&) {
          return std::move(mock_cma_backend_);
        }));
    EXPECT_EQ(mock_backend_factory_.get(),
              audio_manager_->helper_.GetCmaBackendFactory());
  }

  void RunThreadsUntilIdle() {
    task_environment_.RunUntilIdle();
    audio_thread_.FlushForTesting();
  }

  base::Thread audio_thread_;
  base::test::TaskEnvironment task_environment_;
  ::media::FakeAudioLogFactory fake_audio_log_factory_;
  MockCastAudioManagerHelperDelegate mock_delegate_;
  std::unique_ptr<MockCmaBackendFactory> mock_backend_factory_;
  ::media::MockAudioSourceCallback mock_source_callback_;
  std::unique_ptr<MockCmaBackend> mock_cma_backend_;
  std::unique_ptr<MockCmaBackend::AudioDecoder> mock_audio_decoder_;

  std::unique_ptr<CastAudioManager> audio_manager_;
  std::unique_ptr<::media::AudioDeviceInfoAccessorForTests>
      device_info_accessor_;
};

TEST_F(CastAudioManagerTest, HasValidOutputStreamParameters) {
  std::string default_device_id =
      ::media::AudioDeviceDescription::kDefaultDeviceId;
  ::media::AudioParameters params =
      audio_manager_->GetOutputStreamParameters(default_device_id);
  EXPECT_TRUE(params.IsValid());
}

TEST_F(CastAudioManagerTest, CanMakeStream) {
  SetUpBackendAndDecoder();
  ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
      kDefaultAudioParams, "", ::media::AudioManager::LogCallback());
  EXPECT_TRUE(stream->Open());

  EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
  EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
      .WillRepeatedly(Invoke(OnMoreData));
  EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
  stream->Start(&mock_source_callback_);
  RunThreadsUntilIdle();

  stream->Stop();
  RunThreadsUntilIdle();

  stream->Close();
  RunThreadsUntilIdle();
}

#if BUILDFLAG(IS_ANDROID)
TEST_F(CastAudioManagerTest, CanMakeAC3Stream) {
  const ::media::AudioParameters kAC3AudioParams(
      ::media::AudioParameters::AUDIO_BITSTREAM_AC3,
      ::media::CHANNEL_LAYOUT_5_1, ::media::AudioParameters::kAudioCDSampleRate,
      256);
  ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
      kAC3AudioParams, "", ::media::AudioManager::LogCallback());
  EXPECT_TRUE(stream);
  // Only run the rest of the test if the device supports AC3.
  if (stream->Open()) {
    EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
        .WillRepeatedly(Invoke(OnMoreData));
    EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
    stream->Start(&mock_source_callback_);
    RunThreadsUntilIdle();

    stream->Stop();
    RunThreadsUntilIdle();
  }
  stream->Close();
}

#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
TEST_F(CastAudioManagerTest, CanMakeDTSStream) {
  const ::media::AudioParameters kDTSAudioParams(
      ::media::AudioParameters::AUDIO_BITSTREAM_DTS,
      ::media::CHANNEL_LAYOUT_5_1, ::media::AudioParameters::kAudioCDSampleRate,
      256);
  ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
      kDTSAudioParams, "", ::media::AudioManager::LogCallback());
  EXPECT_TRUE(stream);
  // Only run the rest of the test if the device supports DTS.
  if (stream->Open()) {
    EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
        .WillRepeatedly(Invoke(OnMoreData));
    EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
    stream->Start(&mock_source_callback_);
    RunThreadsUntilIdle();

    stream->Stop();
    RunThreadsUntilIdle();
  }
  stream->Close();
}
#endif  // BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO))
#endif  // BUILDFLAG(IS_ANDROID)

TEST_F(CastAudioManagerTest, DISABLED_CanMakeStreamProxy) {
  SetUpBackendAndDecoder();
  ::media::AudioOutputStream* stream =
      audio_manager_->MakeAudioOutputStreamProxy(kDefaultAudioParams, "");
  EXPECT_TRUE(stream->Open());
  RunThreadsUntilIdle();
  EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
  EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
      .WillRepeatedly(Invoke(OnMoreData));
  EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
  stream->Start(&mock_source_callback_);
  RunThreadsUntilIdle();

  stream->Stop();

  stream->Close();
  RunThreadsUntilIdle();
  // TODO(steinbock) Figure out why stream is not unregistering itself from
  // audio_manager_
}

TEST_F(CastAudioManagerTest, CanMakeMixerStream) {
  CreateAudioManagerForTesting(true /* use_mixer */);
  SetUpBackendAndDecoder();
  ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
      kDefaultAudioParams, "", ::media::AudioManager::LogCallback());
  EXPECT_TRUE(stream->Open());

  EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
  EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
      .WillRepeatedly(Invoke(OnMoreData));
  EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
  stream->Start(&mock_source_callback_);
  RunThreadsUntilIdle();

  stream->Stop();
  RunThreadsUntilIdle();

  stream->Close();
}

TEST_F(CastAudioManagerTest, CanMakeCommunicationsStream) {
  CreateAudioManagerForTesting();
  SetUpBackendAndDecoder();

  ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
      kDefaultAudioParams,
      ::media::AudioDeviceDescription::kCommunicationsDeviceId,
      ::media::AudioManager::LogCallback());
  EXPECT_TRUE(stream->Open());

  EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
      .WillRepeatedly(Invoke(OnMoreData));
  EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
  task_environment_.RunUntilIdle();

  stream->Stop();
  task_environment_.RunUntilIdle();

  stream->Close();
}

}  // namespace media
}  // namespace chromecast