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

#include "services/audio/input_stream.h"

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

#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/memory/raw_ptr.h"
#include "base/test/run_until.h"
#include "base/test/task_environment.h"
#include "media/audio/audio_io.h"
#include "media/audio/mock_audio_manager.h"
#include "media/audio/test_audio_thread.h"
#include "media/mojo/mojom/audio_processing.mojom.h"
#include "mojo/public/cpp/bindings/pending_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/receiver.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "mojo/public/cpp/system/functions.h"
#include "services/audio/ml_model_manager.h"
#include "services/audio/stream_factory.h"
#include "services/audio/test/mock_log.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"

using testing::_;
using testing::NiceMock;
using testing::NotNull;
using testing::Return;
using testing::StrictMock;

namespace audio {

namespace {

const uint32_t kDefaultSharedMemoryCount = 10;
const bool kDoEnableAGC = true;
const bool kDoNotEnableAGC = false;
const bool kValidStream = true;
const bool kInvalidStream = false;
const bool kMuted = true;
const bool kNotMuted = false;
const char* kDefaultDeviceId = "default";

class MockMlModelManager : public MlModelManager {
 public:
  std::unique_ptr<MlModelHandle> GetResidualEchoEstimationModel() override {
    model_requested_ = true;
    return nullptr;
  }
  bool model_requested() { return model_requested_; }

 private:
  bool model_requested_ = false;
};

class MockStreamClient : public media::mojom::AudioInputStreamClient {
 public:
  MockStreamClient() = default;

  MockStreamClient(const MockStreamClient&) = delete;
  MockStreamClient& operator=(const MockStreamClient&) = delete;

  mojo::PendingRemote<media::mojom::AudioInputStreamClient> MakeRemote() {
    DCHECK(!receiver_.is_bound());
    mojo::PendingRemote<media::mojom::AudioInputStreamClient> remote;
    receiver_.Bind(remote.InitWithNewPipeAndPassReceiver());
    receiver_.set_disconnect_handler(base::BindOnce(
        &MockStreamClient::BindingConnectionError, base::Unretained(this)));
    return remote;
  }

  void CloseBinding() { receiver_.reset(); }

  MOCK_METHOD1(OnError, void(media::mojom::InputStreamErrorCode));
  MOCK_METHOD1(OnMutedStateChanged, void(bool));
  MOCK_METHOD0(BindingConnectionError, void());

 private:
  mojo::Receiver<media::mojom::AudioInputStreamClient> receiver_{this};
};

class MockStreamObserver : public media::mojom::AudioInputStreamObserver {
 public:
  MockStreamObserver() = default;

  MockStreamObserver(const MockStreamObserver&) = delete;
  MockStreamObserver& operator=(const MockStreamObserver&) = delete;

  mojo::PendingRemote<media::mojom::AudioInputStreamObserver> MakeRemote() {
    DCHECK(!receiver_.is_bound());
    mojo::PendingRemote<media::mojom::AudioInputStreamObserver> remote;
    receiver_.Bind(remote.InitWithNewPipeAndPassReceiver());
    receiver_.set_disconnect_handler(base::BindOnce(
        &MockStreamObserver::BindingConnectionError, base::Unretained(this)));
    return remote;
  }

  void CloseBinding() { receiver_.reset(); }

  MOCK_METHOD0(DidStartRecording, void());

  MOCK_METHOD0(BindingConnectionError, void());

 private:
  mojo::Receiver<media::mojom::AudioInputStreamObserver> receiver_{this};
};

class MockStream : public media::AudioInputStream {
 public:
  MockStream() {}

  MockStream(const MockStream&) = delete;
  MockStream& operator=(const MockStream&) = delete;

  double GetMaxVolume() override { return 1; }

  MOCK_METHOD0(Open, media::AudioInputStream::OpenOutcome());
  MOCK_METHOD1(Start, void(AudioInputCallback*));
  MOCK_METHOD0(Stop, void());
  MOCK_METHOD0(Close, void());
  MOCK_METHOD1(SetVolume, void(double));
  MOCK_METHOD0(GetVolume, double());
  MOCK_METHOD1(SetAutomaticGainControl, bool(bool));
  MOCK_METHOD0(GetAutomaticGainControl, bool());
  MOCK_METHOD0(IsMuted, bool());
  MOCK_METHOD1(SetOutputDeviceForAec, void(const std::string&));
};

}  // namespace

class AudioServiceInputStreamTest : public testing::Test {
 public:
  AudioServiceInputStreamTest()
      : audio_manager_(std::make_unique<media::TestAudioThread>(false)),
        stream_factory_(&audio_manager_,
                        /*aecdump_recording_manager=*/nullptr,
                        &mock_ml_model_manager_),
        stream_factory_receiver_(
            &stream_factory_,
            remote_stream_factory_.BindNewPipeAndPassReceiver()) {}

  AudioServiceInputStreamTest(const AudioServiceInputStreamTest&) = delete;
  AudioServiceInputStreamTest& operator=(const AudioServiceInputStreamTest&) =
      delete;

  ~AudioServiceInputStreamTest() override { audio_manager_.Shutdown(); }

  void SetUp() override {
    mojo::SetDefaultProcessErrorHandler(
        base::BindRepeating(&AudioServiceInputStreamTest::BadMessageCallback,
                            base::Unretained(this)));
  }

  void TearDown() override {
    mojo::SetDefaultProcessErrorHandler(base::NullCallback());
  }

  mojo::PendingRemote<media::mojom::AudioInputStream> CreateStream(
      bool enable_agc) {
    mojo::PendingRemote<media::mojom::AudioInputStream> remote_stream;
    remote_stream_factory_->CreateInputStream(
        remote_stream.InitWithNewPipeAndPassReceiver(), client_.MakeRemote(),
        observer_.MakeRemote(), log_.MakeRemote(), kDefaultDeviceId,
        media::AudioParameters::UnavailableDeviceParams(),
        base::UnguessableToken::Create(), kDefaultSharedMemoryCount, enable_agc,
        std::move(processing_config_),
        base::BindOnce(&AudioServiceInputStreamTest::OnCreated,
                       base::Unretained(this)));
    return remote_stream;
  }

  mojo::PendingRemote<media::mojom::AudioInputStream>
  CreateStreamWithNullptrLog() {
    mojo::PendingRemote<media::mojom::AudioInputStream> remote_stream;
    remote_stream_factory_->CreateInputStream(
        remote_stream.InitWithNewPipeAndPassReceiver(), client_.MakeRemote(),
        observer_.MakeRemote(), mojo::NullRemote(), kDefaultDeviceId,
        media::AudioParameters::UnavailableDeviceParams(),
        base::UnguessableToken::Create(), kDefaultSharedMemoryCount, false,
        std::move(processing_config_),
        base::BindOnce(&AudioServiceInputStreamTest::OnCreated,
                       base::Unretained(this)));
    return remote_stream;
  }

  mojo::PendingRemote<media::mojom::AudioInputStream>
  CreateStreamWithNullptrObserver() {
    mojo::PendingRemote<media::mojom::AudioInputStream> remote_stream;
    remote_stream_factory_->CreateInputStream(
        remote_stream.InitWithNewPipeAndPassReceiver(), client_.MakeRemote(),
        mojo::NullRemote(), log_.MakeRemote(), kDefaultDeviceId,
        media::AudioParameters::UnavailableDeviceParams(),
        base::UnguessableToken::Create(), kDefaultSharedMemoryCount, false,
        std::move(processing_config_),
        base::BindOnce(&AudioServiceInputStreamTest::OnCreated,
                       base::Unretained(this)));
    return remote_stream;
  }

  media::MockAudioManager& audio_manager() { return audio_manager_; }

  MockStreamClient& client() { return client_; }

  MockStreamObserver& observer() { return observer_; }

  MockLog& log() { return log_; }

  void OnCreated(media::mojom::ReadWriteAudioDataPipePtr ptr,
                 bool initially_muted,
                 const std::optional<base::UnguessableToken>& stream_id) {
    EXPECT_EQ(stream_id.has_value(), !!ptr);
    CreatedCallback(!!ptr, initially_muted);
  }

  MOCK_METHOD2(CreatedCallback, void(bool /*valid*/, bool /*initially_muted*/));
  MOCK_METHOD1(BadMessageCallback, void(const std::string&));

 protected:
  base::test::TaskEnvironment scoped_task_env_;
  media::MockAudioManager audio_manager_;
  MockMlModelManager mock_ml_model_manager_;
  media::mojom::AudioProcessingConfigPtr processing_config_ = nullptr;
  StreamFactory stream_factory_;
  mojo::Remote<media::mojom::AudioStreamFactory> remote_stream_factory_;
  mojo::Receiver<media::mojom::AudioStreamFactory> stream_factory_receiver_;
  StrictMock<MockStreamClient> client_;
  StrictMock<MockStreamObserver> observer_;
  NiceMock<MockLog> log_;
};

TEST_F(AudioServiceInputStreamTest, ConstructDestruct) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, ConstructDestructNullptrLog) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStreamWithNullptrLog());
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, ConstructDestructNullptrObserver) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStreamWithNullptrObserver());
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest,
       ConstructStreamAndCloseClientBinding_DestructsStream) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(observer(), BindingConnectionError());
  client().CloseBinding();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest,
       ConstructStreamAndCloseObserverBinding_DestructsStream) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  observer().CloseBinding();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest,
       ConstructStreamAndResetStreamPtr_DestructsStream) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));

  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, Record) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Start(NotNull()));
  EXPECT_CALL(log(), OnStarted());
  EXPECT_CALL(observer(), DidStartRecording());
  remote_stream->Record();
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Stop());
  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, SetVolume) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  const double new_volume = 0.618;
  EXPECT_CALL(mock_stream, SetVolume(new_volume));
  EXPECT_CALL(log(), OnSetVolume(new_volume));
  remote_stream->SetVolume(new_volume);
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, SetNegativeVolume_BadMessage) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(*this, BadMessageCallback(_));
  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream->SetVolume(-0.618);
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, SetVolumeGreaterThanOne_BadMessage) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(*this, BadMessageCallback(_));
  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream->SetVolume(1.618);
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest, CreateStreamWithAGCEnable_PropagateAGC) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(mock_stream, SetAutomaticGainControl(kDoEnableAGC));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest,
       CreateInitiallyMutedStream_PropagateInitiallyMuted) {
  NiceMock<MockStream> mock_stream;
  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kMuted));
  EXPECT_CALL(log(), OnCreated(_, _));
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kMuted));
  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoEnableAGC));
  base::RunLoop().RunUntilIdle();

  EXPECT_CALL(log(), OnClosed());
  EXPECT_CALL(mock_stream, Close());
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  base::RunLoop().RunUntilIdle();
}

TEST_F(AudioServiceInputStreamTest,
       ConstructWithStreamCreationFailure_SignalsError) {
  // By default, MockAudioManager fails to create a stream.

  mojo::Remote<media::mojom::AudioInputStream> remote_stream(
      CreateStream(kDoNotEnableAGC));

  EXPECT_CALL(*this, CreatedCallback(kInvalidStream, kNotMuted));
  EXPECT_CALL(log(), OnError());
  EXPECT_CALL(client(), OnError(media::mojom::InputStreamErrorCode::kUnknown));
  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  base::RunLoop().RunUntilIdle();
}

#if BUILDFLAG(CHROME_WIDE_ECHO_CANCELLATION)
TEST_F(AudioServiceInputStreamTest, ResidualEchoEstimationModelRequested) {
  media::AudioProcessingSettings settings;
  settings.echo_cancellation = true;  // Enable some processing
  auto processing_config = media::mojom::AudioProcessingConfig::New();
  processing_config->settings = settings;
  mojo::Remote<media::mojom::AudioProcessorControls> controls_remote;
  processing_config->controls_receiver =
      controls_remote.BindNewPipeAndPassReceiver();

  // We don't strictly need to check the CreatedCallback for this test's purpose
  // but it avoids the print of a warning.
  EXPECT_CALL(*this, CreatedCallback(kValidStream, kNotMuted));

  // Setup MockAudioManager expectations
  NiceMock<MockStream> mock_stream;
  media::AudioParameters expected_params(
      media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
      media::ChannelLayoutConfig::Stereo(), 48000, 480);

  audio_manager().SetInputStreamParameters(expected_params);

  audio_manager().SetMakeInputStreamCB(base::BindRepeating(
      [](media::AudioInputStream* stream, const media::AudioParameters& params,
         const std::string& device_id) { return stream; },
      &mock_stream));

  EXPECT_CALL(mock_stream, Open())
      .WillOnce(Return(MockStream::OpenOutcome::kSuccess));
  EXPECT_CALL(mock_stream, IsMuted()).WillOnce(Return(kNotMuted));
  EXPECT_CALL(log(), OnCreated(_, _));

  // Call CreateInputStream directly
  mojo::PendingRemote<media::mojom::AudioInputStream> remote_stream;
  remote_stream_factory_->CreateInputStream(
      remote_stream.InitWithNewPipeAndPassReceiver(), client_.MakeRemote(),
      observer_.MakeRemote(), log_.MakeRemote(), kDefaultDeviceId,
      expected_params, base::UnguessableToken::Create(),
      kDefaultSharedMemoryCount, /*enable_agc=*/false,
      std::move(processing_config),
      base::BindOnce(&AudioServiceInputStreamTest::OnCreated,
                     base::Unretained(this)));

  EXPECT_CALL(client(), BindingConnectionError());
  EXPECT_CALL(observer(), BindingConnectionError());
  remote_stream.reset();
  EXPECT_TRUE(base::test::RunUntil(
      [&]() { return mock_ml_model_manager_.model_requested(); }));
}
#endif

}  // namespace audio