blob: 5401a18ce066a662e5dafbb33b1ffaf4af8d5316 [file] [log] [blame]
// Copyright 2018 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.
#include "content/browser/media/forwarding_audio_stream_factory.h"
#include <memory>
#include <utility>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/mock_callback.h"
#include "base/unguessable_token.h"
#include "content/common/media/renderer_audio_input_stream_factory.mojom.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/test_renderer_host.h"
#include "media/base/audio_parameters.h"
#include "media/mojo/interfaces/audio_output_stream.mojom.h"
#include "mojo/public/cpp/bindings/associated_binding.h"
#include "mojo/public/cpp/bindings/binding.h"
#include "mojo/public/cpp/bindings/interface_request.h"
#include "services/audio/public/cpp/fake_stream_factory.h"
#include "services/audio/public/mojom/constants.mojom.h"
#include "services/audio/public/mojom/stream_factory.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::Test;
using ::testing::Mock;
using ::testing::NotNull;
using ::testing::StrictMock;
using ::testing::InSequence;
namespace content {
namespace {
class MockStreamFactory : public audio::FakeStreamFactory,
public audio::mojom::LocalMuter {
public:
MockStreamFactory() : muter_binding_(this) {}
~MockStreamFactory() final {}
bool IsConnected() {
return binding_ && !binding_.handle().QuerySignalsState().peer_closed();
}
bool IsMuterConnected() { return muter_binding_.is_bound(); }
private:
void BindMuter(audio::mojom::LocalMuterAssociatedRequest request,
const base::UnguessableToken& group_id) final {
muter_binding_.Bind(std::move(request));
muter_binding_.set_connection_error_handler(base::BindOnce(
&MockStreamFactory::MuterUnbound, base::Unretained(this)));
}
void MuterUnbound() { muter_binding_.Close(); }
mojo::AssociatedBinding<audio::mojom::LocalMuter> muter_binding_;
DISALLOW_COPY_AND_ASSIGN(MockStreamFactory);
};
class MockBroker : public AudioStreamBroker {
public:
explicit MockBroker(RenderFrameHost* rfh)
: AudioStreamBroker(rfh->GetProcess()->GetID(), rfh->GetRoutingID()),
weak_factory_(this) {}
~MockBroker() override {}
MOCK_METHOD1(CreateStream, void(audio::mojom::StreamFactory* factory));
// Can be used to verify that |this| has been destructed.
base::WeakPtr<MockBroker> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
DeleterCallback deleter;
private:
base::WeakPtrFactory<MockBroker> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(MockBroker);
};
class MockBrokerFactory : public AudioStreamBrokerFactory {
public:
MockBrokerFactory() {}
~MockBrokerFactory() final {
EXPECT_TRUE(prepared_input_stream_brokers_.empty())
<< "Input broker creation was expected but didn't happen";
EXPECT_TRUE(prepared_output_stream_brokers_.empty())
<< "Output broker creation was expected but didn't happen";
}
void ExpectInputStreamBrokerCreation(std::unique_ptr<MockBroker> broker) {
prepared_input_stream_brokers_.push(std::move(broker));
}
void ExpectOutputStreamBrokerCreation(std::unique_ptr<MockBroker> broker) {
prepared_output_stream_brokers_.push(std::move(broker));
}
void ExpectLoopbackStreamBrokerCreation(std::unique_ptr<MockBroker> broker) {
prepared_loopback_stream_brokers_.push(std::move(broker));
}
std::unique_ptr<AudioStreamBroker> CreateAudioInputStreamBroker(
int render_process_id,
int render_frame_id,
const std::string& device_id,
const media::AudioParameters& params,
uint32_t shared_memory_count,
media::UserInputMonitorBase* user_input_monitor,
bool enable_agc,
audio::mojom::AudioProcessingConfigPtr processing_config,
AudioStreamBroker::DeleterCallback deleter,
mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client)
final {
std::unique_ptr<MockBroker> prepared_broker =
std::move(prepared_input_stream_brokers_.front());
prepared_input_stream_brokers_.pop();
CHECK_NE(nullptr, prepared_broker.get());
EXPECT_EQ(render_process_id, prepared_broker->render_process_id());
EXPECT_EQ(render_frame_id, prepared_broker->render_frame_id());
prepared_broker->deleter = std::move(deleter);
return std::move(prepared_broker);
}
std::unique_ptr<AudioStreamBroker> CreateAudioOutputStreamBroker(
int render_process_id,
int render_frame_id,
int stream_id,
const std::string& output_device_id,
const media::AudioParameters& params,
const base::UnguessableToken& group_id,
const base::Optional<base::UnguessableToken>& processing_id,
AudioStreamBroker::DeleterCallback deleter,
media::mojom::AudioOutputStreamProviderClientPtr client) final {
std::unique_ptr<MockBroker> prepared_broker =
std::move(prepared_output_stream_brokers_.front());
prepared_output_stream_brokers_.pop();
CHECK_NE(nullptr, prepared_broker.get());
EXPECT_EQ(render_process_id, prepared_broker->render_process_id());
EXPECT_EQ(render_frame_id, prepared_broker->render_frame_id());
prepared_broker->deleter = std::move(deleter);
return std::move(prepared_broker);
}
std::unique_ptr<AudioStreamBroker> CreateAudioLoopbackStreamBroker(
int render_process_id,
int render_frame_id,
AudioStreamBroker::LoopbackSource* source,
const media::AudioParameters& params,
uint32_t shared_memory_count,
bool mute_source,
AudioStreamBroker::DeleterCallback deleter,
mojom::RendererAudioInputStreamFactoryClientPtr renderer_factory_client)
final {
std::unique_ptr<MockBroker> prepared_broker =
std::move(prepared_loopback_stream_brokers_.front());
prepared_loopback_stream_brokers_.pop();
CHECK_NE(nullptr, prepared_broker.get());
EXPECT_EQ(render_process_id, prepared_broker->render_process_id());
EXPECT_EQ(render_frame_id, prepared_broker->render_frame_id());
prepared_broker->deleter = std::move(deleter);
return std::move(prepared_broker);
}
private:
base::queue<std::unique_ptr<MockBroker>> prepared_loopback_stream_brokers_;
base::queue<std::unique_ptr<MockBroker>> prepared_input_stream_brokers_;
base::queue<std::unique_ptr<MockBroker>> prepared_output_stream_brokers_;
DISALLOW_COPY_AND_ASSIGN(MockBrokerFactory);
};
class MockLoopbackSink : public AudioStreamBroker::LoopbackSink {
public:
MockLoopbackSink() {}
~MockLoopbackSink() override {}
MOCK_METHOD0(OnSourceGone, void());
private:
DISALLOW_COPY_AND_ASSIGN(MockLoopbackSink);
};
class ForwardingAudioStreamFactoryTest : public RenderViewHostTestHarness {
public:
ForwardingAudioStreamFactoryTest()
: connector_(service_manager::Connector::Create(&connector_request_)),
broker_factory_(std::make_unique<MockBrokerFactory>()) {
connector_->OverrideBinderForTesting(
service_manager::ServiceFilter::ByName(audio::mojom::kServiceName),
audio::mojom::StreamFactory::Name_,
base::BindRepeating(&ForwardingAudioStreamFactoryTest::BindFactory,
base::Unretained(this)));
}
~ForwardingAudioStreamFactoryTest() override {}
void SetUp() override {
RenderViewHostTestHarness::SetUp();
RenderFrameHostTester::For(main_rfh())->InitializeRenderFrameIfNeeded();
other_rfh_ =
RenderFrameHostTester::For(main_rfh())->AppendChild("other_rfh");
}
void BindFactory(mojo::ScopedMessagePipeHandle factory_request) {
stream_factory_.binding_.Bind(
audio::mojom::StreamFactoryRequest(std::move(factory_request)));
}
base::WeakPtr<MockBroker> ExpectLoopbackBrokerConstruction(
RenderFrameHost* rfh) {
auto broker = std::make_unique<StrictMock<MockBroker>>(rfh);
auto weak_broker = broker->GetWeakPtr();
broker_factory_->ExpectLoopbackStreamBrokerCreation(std::move(broker));
return weak_broker;
}
base::WeakPtr<MockBroker> ExpectInputBrokerConstruction(
RenderFrameHost* rfh) {
auto broker = std::make_unique<StrictMock<MockBroker>>(rfh);
auto weak_broker = broker->GetWeakPtr();
broker_factory_->ExpectInputStreamBrokerCreation(std::move(broker));
return weak_broker;
}
base::WeakPtr<MockBroker> ExpectOutputBrokerConstruction(
RenderFrameHost* rfh) {
auto broker = std::make_unique<StrictMock<MockBroker>>(rfh);
auto weak_broker = broker->GetWeakPtr();
broker_factory_->ExpectOutputStreamBrokerCreation(std::move(broker));
return weak_broker;
}
RenderFrameHost* other_rfh() { return other_rfh_; }
static const char kInputDeviceId[];
static const char kOutputDeviceId[];
static const media::AudioParameters kParams;
static const uint32_t kSharedMemoryCount;
static const bool kEnableAgc;
MockStreamFactory stream_factory_;
service_manager::mojom::ConnectorRequest connector_request_;
std::unique_ptr<service_manager::Connector> connector_;
RenderFrameHost* other_rfh_;
std::unique_ptr<MockBrokerFactory> broker_factory_;
};
const char ForwardingAudioStreamFactoryTest::kInputDeviceId[] =
"test input device id";
const char ForwardingAudioStreamFactoryTest::kOutputDeviceId[] =
"test output device id";
const media::AudioParameters ForwardingAudioStreamFactoryTest::kParams =
media::AudioParameters::UnavailableDeviceParams();
const uint32_t ForwardingAudioStreamFactoryTest::kSharedMemoryCount = 10;
const bool ForwardingAudioStreamFactoryTest::kEnableAgc = false;
const bool kMuteSource = true;
} // namespace
TEST_F(ForwardingAudioStreamFactoryTest, CreateInputStream_CreatesInputStream) {
mojom::RendererAudioInputStreamFactoryClientPtr client;
base::WeakPtr<MockBroker> broker = ExpectInputBrokerConstruction(main_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
EXPECT_CALL(*broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateInputStream(main_rfh()->GetProcess()->GetID(),
main_rfh()->GetRoutingID(), kInputDeviceId,
kParams, kSharedMemoryCount, kEnableAgc,
nullptr, std::move(client));
}
TEST_F(ForwardingAudioStreamFactoryTest,
CreateLoopbackStream_CreatesLoopbackStream) {
std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
mojom::RendererAudioInputStreamFactoryClientPtr client;
base::WeakPtr<MockBroker> broker =
ExpectLoopbackBrokerConstruction(main_rfh());
std::unique_ptr<service_manager::Connector> other_connector =
connector_->Clone();
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
ForwardingAudioStreamFactory source_factory(
source_contents.get(), nullptr /*user_input_monitor*/,
std::move(other_connector), std::make_unique<MockBrokerFactory>());
EXPECT_CALL(*broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateLoopbackStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
std::move(client));
}
TEST_F(ForwardingAudioStreamFactoryTest,
CreateOutputStream_CreatesOutputStream) {
media::mojom::AudioOutputStreamProviderClientPtr client;
base::WeakPtr<MockBroker> broker = ExpectOutputBrokerConstruction(main_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
EXPECT_CALL(*broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
}
TEST_F(ForwardingAudioStreamFactoryTest,
InputBrokerDeleterCalled_DestroysInputStream) {
mojom::RendererAudioInputStreamFactoryClientPtr client;
base::WeakPtr<MockBroker> main_rfh_broker =
ExpectInputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_broker =
ExpectInputBrokerConstruction(other_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
{
EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateInputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
std::move(client));
testing::Mock::VerifyAndClear(&*main_rfh_broker);
}
{
EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateInputStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
std::move(client));
testing::Mock::VerifyAndClear(&*other_rfh_broker);
}
std::move(other_rfh_broker->deleter).Run(&*other_rfh_broker);
EXPECT_FALSE(other_rfh_broker)
<< "Input broker should be destructed when deleter is called.";
EXPECT_TRUE(main_rfh_broker);
}
TEST_F(ForwardingAudioStreamFactoryTest,
LoopbackBrokerDeleterCalled_DestroysInputStream) {
std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
mojom::RendererAudioInputStreamFactoryClientPtr client;
base::WeakPtr<MockBroker> main_rfh_broker =
ExpectLoopbackBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_broker =
ExpectLoopbackBrokerConstruction(other_rfh());
std::unique_ptr<service_manager::Connector> other_connector =
connector_->Clone();
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
ForwardingAudioStreamFactory source_factory(
source_contents.get(), nullptr /*user_input_monitor*/,
std::move(other_connector), std::make_unique<MockBrokerFactory>());
{
EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateLoopbackStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
std::move(client));
testing::Mock::VerifyAndClear(&*main_rfh_broker);
}
{
EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateLoopbackStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
std::move(client));
testing::Mock::VerifyAndClear(&*other_rfh_broker);
}
std::move(other_rfh_broker->deleter).Run(&*other_rfh_broker);
EXPECT_FALSE(other_rfh_broker)
<< "Loopback broker should be destructed when deleter is called.";
EXPECT_TRUE(main_rfh_broker);
}
TEST_F(ForwardingAudioStreamFactoryTest,
OutputBrokerDeleterCalled_DestroysOutputStream) {
media::mojom::AudioOutputStreamProviderClientPtr client;
base::WeakPtr<MockBroker> main_rfh_broker =
ExpectOutputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_broker =
ExpectOutputBrokerConstruction(other_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
{
EXPECT_CALL(*main_rfh_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
testing::Mock::VerifyAndClear(&*main_rfh_broker);
}
{
EXPECT_CALL(*other_rfh_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
testing::Mock::VerifyAndClear(&*other_rfh_broker);
}
std::move(other_rfh_broker->deleter).Run(&*other_rfh_broker);
EXPECT_FALSE(other_rfh_broker)
<< "Output broker should be destructed when deleter is called.";
EXPECT_TRUE(main_rfh_broker);
}
TEST_F(ForwardingAudioStreamFactoryTest, DestroyFrame_DestroysRelatedStreams) {
std::unique_ptr<WebContents> source_contents = CreateTestWebContents();
mojom::RendererAudioInputStreamFactoryClientPtr input_client;
base::WeakPtr<MockBroker> main_rfh_input_broker =
ExpectInputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_input_broker =
ExpectInputBrokerConstruction(other_rfh());
base::WeakPtr<MockBroker> main_rfh_loopback_broker =
ExpectLoopbackBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_loopback_broker =
ExpectLoopbackBrokerConstruction(other_rfh());
media::mojom::AudioOutputStreamProviderClientPtr output_client;
base::WeakPtr<MockBroker> main_rfh_output_broker =
ExpectOutputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_output_broker =
ExpectOutputBrokerConstruction(other_rfh());
std::unique_ptr<service_manager::Connector> other_connector =
connector_->Clone();
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
ForwardingAudioStreamFactory source_factory(
source_contents.get(), nullptr /*user_input_monitor*/,
std::move(other_connector), std::make_unique<MockBrokerFactory>());
{
EXPECT_CALL(*main_rfh_input_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateInputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
std::move(input_client));
testing::Mock::VerifyAndClear(&*main_rfh_input_broker);
}
{
EXPECT_CALL(*other_rfh_input_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateInputStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
std::move(input_client));
testing::Mock::VerifyAndClear(&*other_rfh_input_broker);
}
{
EXPECT_CALL(*main_rfh_loopback_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateLoopbackStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
std::move(input_client));
testing::Mock::VerifyAndClear(&*main_rfh_loopback_broker);
}
{
EXPECT_CALL(*other_rfh_loopback_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateLoopbackStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
source_factory.core(), kParams, kSharedMemoryCount, kMuteSource,
std::move(input_client));
testing::Mock::VerifyAndClear(&*other_rfh_loopback_broker);
}
{
EXPECT_CALL(*main_rfh_output_broker, CreateStream(NotNull()));
mojo::MakeRequest(&output_client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
testing::Mock::VerifyAndClear(&*main_rfh_output_broker);
}
{
EXPECT_CALL(*other_rfh_output_broker, CreateStream(NotNull()));
mojo::MakeRequest(&output_client);
factory.core()->CreateOutputStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
testing::Mock::VerifyAndClear(&*other_rfh_output_broker);
}
factory.FrameDeleted(other_rfh());
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(other_rfh_input_broker)
<< "Input broker should be destructed when owning frame is destructed.";
EXPECT_TRUE(main_rfh_input_broker);
EXPECT_FALSE(other_rfh_loopback_broker) << "Loopback broker should be "
"destructed when owning frame is "
"destructed.";
EXPECT_TRUE(main_rfh_loopback_broker);
EXPECT_FALSE(other_rfh_output_broker)
<< "Output broker should be destructed when owning frame is destructed.";
EXPECT_TRUE(main_rfh_output_broker);
}
TEST_F(ForwardingAudioStreamFactoryTest, DestroyWebContents_DestroysStreams) {
mojom::RendererAudioInputStreamFactoryClientPtr input_client;
base::WeakPtr<MockBroker> input_broker =
ExpectInputBrokerConstruction(main_rfh());
media::mojom::AudioOutputStreamProviderClientPtr output_client;
base::WeakPtr<MockBroker> output_broker =
ExpectOutputBrokerConstruction(main_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
EXPECT_CALL(*input_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateInputStream(main_rfh()->GetProcess()->GetID(),
main_rfh()->GetRoutingID(), kInputDeviceId,
kParams, kSharedMemoryCount, kEnableAgc,
nullptr, std::move(input_client));
EXPECT_CALL(*output_broker, CreateStream(NotNull()));
mojo::MakeRequest(&output_client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
DeleteContents();
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(input_broker) << "Input broker should be destructed when owning "
"WebContents is destructed.";
EXPECT_FALSE(output_broker)
<< "Output broker should be destructed when owning "
"WebContents is destructed.";
}
TEST_F(ForwardingAudioStreamFactoryTest, LastStreamDeleted_ClearsFactoryPtr) {
mojom::RendererAudioInputStreamFactoryClientPtr input_client;
base::WeakPtr<MockBroker> main_rfh_input_broker =
ExpectInputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_input_broker =
ExpectInputBrokerConstruction(other_rfh());
media::mojom::AudioOutputStreamProviderClientPtr output_client;
base::WeakPtr<MockBroker> main_rfh_output_broker =
ExpectOutputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> other_rfh_output_broker =
ExpectOutputBrokerConstruction(other_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
{
EXPECT_CALL(*main_rfh_input_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateInputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
std::move(input_client));
testing::Mock::VerifyAndClear(&*main_rfh_input_broker);
}
{
EXPECT_CALL(*other_rfh_input_broker, CreateStream(NotNull()));
mojo::MakeRequest(&input_client);
factory.core()->CreateInputStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
kInputDeviceId, kParams, kSharedMemoryCount, kEnableAgc, nullptr,
std::move(input_client));
testing::Mock::VerifyAndClear(&*other_rfh_input_broker);
}
{
EXPECT_CALL(*main_rfh_output_broker, CreateStream(NotNull()));
mojo::MakeRequest(&output_client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
testing::Mock::VerifyAndClear(&*main_rfh_output_broker);
}
{
EXPECT_CALL(*other_rfh_output_broker, CreateStream(NotNull()));
mojo::MakeRequest(&output_client);
factory.core()->CreateOutputStream(
other_rfh()->GetProcess()->GetID(), other_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(output_client));
testing::Mock::VerifyAndClear(&*other_rfh_output_broker);
}
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(stream_factory_.IsConnected());
std::move(other_rfh_input_broker->deleter).Run(&*other_rfh_input_broker);
base::RunLoop().RunUntilIdle();
// Connection should still be open, since there are still streams left.
EXPECT_TRUE(stream_factory_.IsConnected());
std::move(main_rfh_input_broker->deleter).Run(&*main_rfh_input_broker);
base::RunLoop().RunUntilIdle();
// Connection should still be open, since there are still streams left.
EXPECT_TRUE(stream_factory_.IsConnected());
std::move(other_rfh_output_broker->deleter).Run(&*other_rfh_output_broker);
base::RunLoop().RunUntilIdle();
// Connection should still be open, since there's still a stream left.
EXPECT_TRUE(stream_factory_.IsConnected());
std::move(main_rfh_output_broker->deleter).Run(&*main_rfh_output_broker);
base::RunLoop().RunUntilIdle();
// Now there are no streams left, connection should be broken.
EXPECT_FALSE(stream_factory_.IsConnected());
}
TEST_F(ForwardingAudioStreamFactoryTest,
MuteNoOutputStreams_DoesNotConnectMuter) {
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
EXPECT_FALSE(factory.IsMuted());
factory.SetMuted(true);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_FALSE(stream_factory_.IsConnected());
EXPECT_FALSE(stream_factory_.IsMuterConnected());
factory.SetMuted(false);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(factory.IsMuted());
EXPECT_FALSE(stream_factory_.IsConnected());
EXPECT_FALSE(stream_factory_.IsMuterConnected());
}
TEST_F(ForwardingAudioStreamFactoryTest, MuteWithOutputStream_ConnectsMuter) {
media::mojom::AudioOutputStreamProviderClientPtr client;
base::WeakPtr<MockBroker> broker = ExpectOutputBrokerConstruction(main_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
EXPECT_CALL(*broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClear(&*broker);
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_FALSE(factory.IsMuted());
factory.SetMuted(true);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_TRUE(stream_factory_.IsMuterConnected());
factory.SetMuted(false);
base::RunLoop().RunUntilIdle();
EXPECT_FALSE(factory.IsMuted());
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_FALSE(stream_factory_.IsMuterConnected());
}
TEST_F(ForwardingAudioStreamFactoryTest,
WhenMuting_ConnectedWhenOutputStreamExists) {
media::mojom::AudioOutputStreamProviderClientPtr client;
base::WeakPtr<MockBroker> broker = ExpectOutputBrokerConstruction(main_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
EXPECT_FALSE(stream_factory_.IsConnected());
EXPECT_FALSE(factory.IsMuted());
factory.SetMuted(true);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_FALSE(stream_factory_.IsConnected());
EXPECT_FALSE(stream_factory_.IsMuterConnected());
EXPECT_CALL(*broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_TRUE(stream_factory_.IsMuterConnected());
testing::Mock::VerifyAndClear(&*broker);
std::move(broker->deleter).Run(&*broker);
EXPECT_FALSE(broker);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_FALSE(stream_factory_.IsConnected());
EXPECT_FALSE(stream_factory_.IsMuterConnected());
}
TEST_F(ForwardingAudioStreamFactoryTest,
WhenMuting_AddRemoveSecondStream_DoesNotChangeMuting) {
media::mojom::AudioOutputStreamProviderClientPtr client;
base::WeakPtr<MockBroker> broker = ExpectOutputBrokerConstruction(main_rfh());
base::WeakPtr<MockBroker> another_broker =
ExpectOutputBrokerConstruction(main_rfh());
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
{
EXPECT_CALL(*broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClear(&*broker);
}
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_FALSE(factory.IsMuted());
factory.SetMuted(true);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_TRUE(stream_factory_.IsMuterConnected());
{
EXPECT_CALL(*another_broker, CreateStream(NotNull()));
mojo::MakeRequest(&client);
factory.core()->CreateOutputStream(
main_rfh()->GetProcess()->GetID(), main_rfh()->GetRoutingID(),
kOutputDeviceId, kParams, base::nullopt, std::move(client));
base::RunLoop().RunUntilIdle();
testing::Mock::VerifyAndClear(&*another_broker);
}
EXPECT_TRUE(factory.IsMuted());
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_TRUE(stream_factory_.IsMuterConnected());
std::move(another_broker->deleter).Run(&*another_broker);
EXPECT_FALSE(another_broker);
base::RunLoop().RunUntilIdle();
EXPECT_TRUE(factory.IsMuted());
EXPECT_TRUE(stream_factory_.IsConnected());
EXPECT_TRUE(stream_factory_.IsMuterConnected());
}
TEST_F(ForwardingAudioStreamFactoryTest,
Destruction_CallsOnSourceGoneOnRegisteredLoopbackSinks) {
StrictMock<MockLoopbackSink> sink1;
StrictMock<MockLoopbackSink> sink2;
// We remove |sink1| before |factory| is destructed, so it shouldn't be
// called.
EXPECT_CALL(sink2, OnSourceGone());
{
ForwardingAudioStreamFactory factory(
web_contents(), nullptr /*user_input_monitor*/, std::move(connector_),
std::move(broker_factory_));
factory.core()->AddLoopbackSink(&sink1);
factory.core()->AddLoopbackSink(&sink2);
factory.core()->RemoveLoopbackSink(&sink1);
}
base::RunLoop().RunUntilIdle();
}
} // namespace content