blob: acbbbffe035ef208384a65aea5782510fd33500e [file] [log] [blame]
// Copyright 2017 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 "third_party/blink/renderer/modules/media/audio/mojo_audio_input_ipc.h"
#include <algorithm>
#include <memory>
#include <string>
#include <utility>
#include "base/bind.h"
#include "base/optional.h"
#include "base/run_loop.h"
#include "base/test/gtest_util.h"
#include "media/audio/audio_device_description.h"
#include "media/base/audio_parameters.h"
#include "media/mojo/mojom/audio_data_pipe.mojom-blink.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/buffer.h"
#include "mojo/public/cpp/system/platform_handle.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using testing::_;
using testing::AtLeast;
using testing::Invoke;
using testing::Mock;
using testing::StrictMock;
namespace blink {
namespace {
const size_t kMemoryLength = 4321;
const size_t kTotalSegments = 1;
const double kNewVolume = 0.271828;
const char kOutputDeviceId[] = "2345";
media::AudioParameters Params() {
return media::AudioParameters::UnavailableDeviceParams();
}
media::AudioSourceParameters SourceParams() {
return media::AudioSourceParameters(
base::UnguessableToken::Deserialize(1234, 5678));
}
class MockStream : public media::mojom::blink::AudioInputStream {
public:
MOCK_METHOD0(Record, void());
MOCK_METHOD1(SetVolume, void(double));
};
class MockDelegate : public media::AudioInputIPCDelegate {
public:
MockDelegate() = default;
~MockDelegate() override = default;
void OnStreamCreated(base::ReadOnlySharedMemoryRegion mem_handle,
base::SyncSocket::ScopedHandle socket_handle,
bool initially_muted) override {
GotOnStreamCreated(initially_muted);
}
MOCK_METHOD1(GotOnStreamCreated, void(bool initially_muted));
MOCK_METHOD0(OnError, void());
MOCK_METHOD1(OnMuted, void(bool));
MOCK_METHOD0(OnIPCClosed, void());
};
class FakeStreamCreator {
public:
FakeStreamCreator(media::mojom::blink::AudioInputStream* stream,
bool initially_muted)
: stream_(stream),
receiver_(stream_),
initially_muted_(initially_muted) {}
void Create(
const media::AudioSourceParameters& source_params,
mojo::PendingRemote<mojom::blink::RendererAudioInputStreamFactoryClient>
factory_client,
const media::AudioParameters& params,
bool automatic_gain_control,
uint32_t total_segments) {
EXPECT_FALSE(receiver_.is_bound());
EXPECT_NE(stream_, nullptr);
EXPECT_EQ(source_params.session_id, SourceParams().session_id);
factory_client_.reset();
factory_client_.Bind(std::move(factory_client));
base::CancelableSyncSocket foreign_socket;
EXPECT_TRUE(
base::CancelableSyncSocket::CreatePair(&socket_, &foreign_socket));
factory_client_->StreamCreated(
receiver_.BindNewPipeAndPassRemote(),
stream_client_.BindNewPipeAndPassReceiver(),
{base::in_place,
base::ReadOnlySharedMemoryRegion::Create(kMemoryLength).region,
mojo::PlatformHandle(foreign_socket.Take())},
initially_muted_, base::UnguessableToken::Create());
}
MojoAudioInputIPC::StreamCreatorCB GetCallback() {
return base::BindRepeating(&FakeStreamCreator::Create,
base::Unretained(this));
}
void Rearm() {
stream_client_.reset();
receiver_.reset();
socket_.Close();
}
void SignalError() {
ASSERT_TRUE(stream_client_);
stream_client_->OnError();
}
private:
media::mojom::blink::AudioInputStream* stream_;
mojo::Remote<media::mojom::blink::AudioInputStreamClient> stream_client_;
mojo::Remote<mojom::blink::RendererAudioInputStreamFactoryClient>
factory_client_;
mojo::Receiver<media::mojom::blink::AudioInputStream> receiver_;
bool initially_muted_;
base::CancelableSyncSocket socket_;
};
void AssociateOutputForAec(const base::UnguessableToken& stream_id,
const std::string& output_device_id) {
EXPECT_FALSE(stream_id.is_empty());
EXPECT_EQ(output_device_id, kOutputDeviceId);
}
} // namespace
TEST(MojoAudioInputIPC, OnStreamCreated_Propagates) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, false);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
EXPECT_CALL(delegate, GotOnStreamCreated(false));
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
TEST(MojoAudioInputIPC, FactoryDisconnected_SendsError) {
StrictMock<MockDelegate> delegate;
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(),
base::BindRepeating(
[](const media::AudioSourceParameters&,
mojo::PendingRemote<
mojom::blink::RendererAudioInputStreamFactoryClient>
factory_client,
const media::AudioParameters& params,
bool automatic_gain_control, uint32_t total_segments) {}),
base::BindRepeating(&AssociateOutputForAec));
EXPECT_CALL(delegate, OnError());
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
TEST(MojoAudioInputIPC, OnStreamCreated_PropagatesInitiallyMuted) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, true);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
EXPECT_CALL(delegate, GotOnStreamCreated(true));
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
TEST(MojoAudioInputIPC, IsReusable) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, false);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
for (int i = 0; i < 5; ++i) {
creator.Rearm();
EXPECT_CALL(delegate, GotOnStreamCreated(_));
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate);
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
}
TEST(MojoAudioInputIPC, IsReusableAfterError) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, false);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
for (int i = 0; i < 5; ++i) {
creator.Rearm();
EXPECT_CALL(delegate, GotOnStreamCreated(_));
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate);
EXPECT_CALL(delegate, OnError());
creator.SignalError();
base::RunLoop().RunUntilIdle();
Mock::VerifyAndClearExpectations(&delegate);
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
}
TEST(MojoAudioInputIPC, Record_Records) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, false);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
EXPECT_CALL(delegate, GotOnStreamCreated(_));
EXPECT_CALL(stream, Record());
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
ipc->RecordStream();
base::RunLoop().RunUntilIdle();
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
TEST(MojoAudioInputIPC, SetVolume_SetsVolume) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, false);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
EXPECT_CALL(delegate, GotOnStreamCreated(_));
EXPECT_CALL(stream, SetVolume(kNewVolume));
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
ipc->SetVolume(kNewVolume);
base::RunLoop().RunUntilIdle();
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
TEST(MojoAudioInputIPC, SetOutputDeviceForAec_AssociatesInputAndOutputForAec) {
StrictMock<MockStream> stream;
StrictMock<MockDelegate> delegate;
FakeStreamCreator creator(&stream, false);
const std::unique_ptr<media::AudioInputIPC> ipc =
std::make_unique<MojoAudioInputIPC>(
SourceParams(), creator.GetCallback(),
base::BindRepeating(&AssociateOutputForAec));
EXPECT_CALL(delegate, GotOnStreamCreated(_));
ipc->CreateStream(&delegate, Params(), false, kTotalSegments);
base::RunLoop().RunUntilIdle();
ipc->SetOutputDeviceForAec(kOutputDeviceId);
base::RunLoop().RunUntilIdle();
ipc->CloseStream();
base::RunLoop().RunUntilIdle();
}
} // namespace blink