blob: 2052ca880e69f1f0791ed2e9b0fddcd1e62df653 [file] [log] [blame]
// Copyright 2019 The Fuchsia 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 "src/media/audio/audio_core/route_graph.h"
#include <gmock/gmock.h>
#include "src/media/audio/audio_core/audio_driver.h"
#include "src/media/audio/audio_core/format.h"
#include "src/media/audio/audio_core/testing/fake_audio_driver.h"
#include "src/media/audio/audio_core/testing/fake_audio_renderer.h"
#include "src/media/audio/audio_core/testing/stub_device_registry.h"
#include "src/media/audio/audio_core/testing/threading_model_fixture.h"
#include "src/media/audio/audio_core/throttle_output.h"
#include "src/media/audio/audio_core/usage_settings.h"
#include "src/media/audio/lib/logging/logging.h"
using testing::IsEmpty;
using testing::UnorderedElementsAreArray;
namespace media::audio {
namespace {
class FakeAudioObject : public AudioObject {
public:
static fbl::RefPtr<FakeAudioObject> FakeRenderer(bool valid_format = true) {
return fbl::AdoptRef(new FakeAudioObject(AudioObject::Type::AudioRenderer, valid_format));
}
static fbl::RefPtr<FakeAudioObject> FakeCapturer() {
return fbl::AdoptRef(
new FakeAudioObject(AudioObject::Type::AudioCapturer, /*valid_format=*/true));
}
FakeAudioObject(AudioObject::Type object_type, bool valid_format) : AudioObject(object_type) {
if (valid_format) {
format_ = Format::Create({.sample_format = fuchsia::media::AudioSampleFormat::UNSIGNED_8});
}
}
const fbl::RefPtr<Format>& format() const override { return format_; }
std::vector<AudioObject*> SourceLinks() {
std::vector<AudioObject*> source_links;
ForEachSourceLink(
[&source_links](auto& link) { source_links.push_back(link.GetSource().get()); });
return source_links;
}
std::vector<AudioObject*> DestLinks() {
std::vector<AudioObject*> dest_links;
ForEachDestLink([&dest_links](auto& link) { dest_links.push_back(link.GetDest().get()); });
return dest_links;
}
private:
fbl::RefPtr<Format> format_ = nullptr;
};
// TODO(39532): Remove; use a real output class with fake hardware.
class FakeAudioOutput : public AudioOutput {
public:
static fbl::RefPtr<FakeAudioOutput> Create(ThreadingModel* threading_model,
testing::StubDeviceRegistry* device_registry) {
return fbl::AdoptRef(new FakeAudioOutput(threading_model, device_registry));
}
void ApplyGainLimits(fuchsia::media::AudioGainInfo* in_out_info, uint32_t set_flags) override {}
void OnWakeup() override {}
std::optional<FrameSpan> StartMixJob(MixJob* job, zx::time process_start) override {
return std::nullopt;
}
void FinishMixJob(const MixJob& job) override {}
private:
FakeAudioOutput(ThreadingModel* threading_model, testing::StubDeviceRegistry* device_registry)
: AudioOutput(threading_model, device_registry) {}
};
static const RoutingConfig kConfigNoPolicy = RoutingConfig();
class RouteGraphTest : public testing::ThreadingModelFixture {
public:
RouteGraphTest() : RouteGraphTest(kConfigNoPolicy) {}
RouteGraphTest(const RoutingConfig& routing_config)
: under_test_(routing_config),
throttle_output_(ThrottleOutput::Create(&threading_model(), &device_registry_)) {
Logging::Init(-media::audio::SPEW, {"route_graph_test"});
under_test_.SetThrottleOutput(&threading_model(), throttle_output_);
}
struct FakeOutputAndDriver {
fbl::RefPtr<FakeAudioOutput> output;
std::unique_ptr<testing::FakeAudioDriver> fake_driver;
};
FakeOutputAndDriver OutputWithDeviceId(const audio_stream_unique_id_t& device_id) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
zx::channel c1, c2;
ZX_ASSERT(ZX_OK == zx::channel::create(0, &c1, &c2));
auto fake_driver = std::make_unique<testing::FakeAudioDriver>(
std::move(c1), threading_model().FidlDomain().dispatcher());
fake_driver->set_stream_unique_id(device_id);
ZX_ASSERT(ZX_OK == output->driver()->Init(std::move(c2)));
fake_driver->Start();
output->driver()->GetDriverInfo();
RunLoopUntilIdle();
return {output, std::move(fake_driver)};
}
testing::StubDeviceRegistry device_registry_;
RouteGraph under_test_;
fbl::RefPtr<AudioOutput> throttle_output_;
};
TEST_F(RouteGraphTest, RenderersAreUnlinkedWhenHaveNoRoutingProfile) {
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
EXPECT_THAT(renderer->DestLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, RenderersRouteToLastPluggedOutput) {
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({first_output.get()}));
auto later_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(later_output.get());
EXPECT_THAT(renderer->DestLinks(),
UnorderedElementsAreArray(std::vector<AudioObject*>{later_output.get()}));
}
TEST_F(RouteGraphTest, RenderersFallbackWhenOutputRemoved) {
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
auto later_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
under_test_.AddOutput(later_output.get());
under_test_.RemoveOutput(later_output.get());
EXPECT_THAT(renderer->DestLinks(),
UnorderedElementsAreArray(std::vector<AudioObject*>{first_output.get()}));
under_test_.RemoveOutput(first_output.get());
EXPECT_THAT(renderer->DestLinks(),
UnorderedElementsAreArray(std::vector<AudioObject*>{throttle_output_.get()}));
}
TEST_F(RouteGraphTest, RemovingNonLastOutputDoesNotRerouteRenderers) {
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
auto second_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
auto last_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
under_test_.AddOutput(second_output.get());
under_test_.AddOutput(last_output.get());
under_test_.RemoveOutput(second_output.get());
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({last_output.get()}));
under_test_.RemoveOutput(first_output.get());
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({last_output.get()}));
}
TEST_F(RouteGraphTest, RenderersPickUpLastPluggedOutputWhenRoutable) {
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
EXPECT_THAT(renderer->DestLinks(), IsEmpty());
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({first_output.get()}));
}
TEST_F(RouteGraphTest, RenderersAreRemoved) {
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
// Now the renderer should have 3 references:
// 1. Ours in this test.
// 2. The RouteGraph's.
// 3. The ThrottleOutput's (because they are linked).
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({throttle_output_.get()}));
EXPECT_EQ(renderer->ref_count_debug(), 3);
under_test_.RemoveRenderer(renderer.get());
EXPECT_EQ(renderer->ref_count_debug(), 1);
}
TEST_F(RouteGraphTest, CapturersAreUnlinkedWhenHaveNoRoutingProfile) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, CapturersRouteToLastPluggedInput) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto first_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(first_input.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({first_input.get()}));
auto later_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(later_input.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({later_input.get()}));
}
TEST_F(RouteGraphTest, CapturersFallbackWhenInputRemoved) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto first_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
auto later_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(first_input.get());
under_test_.AddInput(later_input.get());
under_test_.RemoveInput(later_input.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({first_input.get()}));
under_test_.RemoveInput(first_input.get());
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, RemovingNonLastInputDoesNotRerouteCapturers) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto first_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
auto second_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
auto last_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(first_input.get());
under_test_.AddInput(second_input.get());
under_test_.AddInput(last_input.get());
under_test_.RemoveInput(first_input.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({last_input.get()}));
under_test_.RemoveInput(second_input.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({last_input.get()}));
}
TEST_F(RouteGraphTest, CapturersPickUpLastPluggedInputWhenRoutable) {
auto first_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(first_input.get());
auto later_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(later_input.get());
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({later_input.get()}));
}
TEST_F(RouteGraphTest, CapturersAreRemoved) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_EQ(capturer->ref_count_debug(), 2);
under_test_.RemoveCapturer(capturer.get());
EXPECT_EQ(capturer->ref_count_debug(), 1);
}
TEST_F(RouteGraphTest, LoopbackCapturersAreUnlinkedWhenHaveNoRoutingProfile) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, LoopbackCapturersRouteToLastPluggedOutput) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({first_output.get()}));
auto later_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(later_output.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({later_output.get()}));
}
TEST_F(RouteGraphTest, LoopbackCapturersFallbackWhenOutputRemoved) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
auto later_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
under_test_.AddOutput(later_output.get());
under_test_.RemoveOutput(later_output.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({first_output.get()}));
under_test_.RemoveOutput(first_output.get());
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, RemovingNonLastOutputDoesNotRerouteLoopbackCapturers) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
auto second_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
auto last_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
under_test_.AddOutput(second_output.get());
under_test_.AddOutput(last_output.get());
under_test_.RemoveOutput(second_output.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({last_output.get()}));
under_test_.RemoveOutput(first_output.get());
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({last_output.get()}));
}
TEST_F(RouteGraphTest, LoopbackCapturersPickUpLastPluggedOutputWhenRoutable) {
auto first_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(first_output.get());
auto later_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(later_output.get());
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({later_output.get()}));
}
TEST_F(RouteGraphTest, LoopbackCapturersAreRemoved) {
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
EXPECT_EQ(capturer->ref_count_debug(), 2);
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_EQ(capturer->ref_count_debug(), 2);
under_test_.RemoveLoopbackCapturer(capturer.get());
EXPECT_EQ(capturer->ref_count_debug(), 1);
}
TEST_F(RouteGraphTest, OutputRouteCategoriesDoNotAffectEachOther) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(output.get());
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
EXPECT_THAT(renderer->DestLinks(),
UnorderedElementsAreArray(std::vector<AudioObject*>{output.get()}));
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({output.get()}));
EXPECT_THAT(renderer->DestLinks(),
UnorderedElementsAreArray(std::vector<AudioObject*>{output.get()}));
}
TEST_F(RouteGraphTest, InputRouteCategoriesDoNotAffectOutputs) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(output.get());
auto first_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(first_input.get());
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({first_input.get()}));
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
EXPECT_THAT(renderer->DestLinks(),
UnorderedElementsAreArray(std::vector<AudioObject*>{output.get()}));
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({first_input.get()}));
}
TEST_F(RouteGraphTest, DoesNotRouteUnroutableRenderer) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(output.get());
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
EXPECT_THAT(renderer->DestLinks(), IsEmpty());
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
auto second_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(second_output.get());
EXPECT_THAT(renderer->DestLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, DoesNotRouteUnroutableCapturer) {
auto input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(input.get());
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto second_input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(second_input.get());
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, DoesNotRouteUnroutableLoopbackCapturer) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(output.get());
auto loopback_capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(loopback_capturer);
EXPECT_THAT(loopback_capturer->SourceLinks(), IsEmpty());
under_test_.SetLoopbackCapturerRoutingProfile(
loopback_capturer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
auto second_output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(second_output.get());
EXPECT_THAT(loopback_capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, AcceptsUnroutableRendererWithInvalidFormat) {
auto renderer = FakeAudioObject::FakeRenderer(/*valid_format=*/false);
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
// Passes by not crashing.
}
TEST_F(RouteGraphTest, UnroutesNewlyUnRoutableRenderer) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(output.get());
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
EXPECT_THAT(renderer->DestLinks(), IsEmpty());
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
EXPECT_THAT(renderer->DestLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, UnroutesNewlyUnRoutableCapturer) {
auto input = AudioInput::Create(zx::channel(), &threading_model(), &device_registry_);
under_test_.AddInput(input.get());
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddCapturer(capturer);
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
under_test_.SetCapturerRoutingProfile(
capturer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), IsEmpty());
}
TEST_F(RouteGraphTest, UnroutesNewlyUnRoutableLoopbackCapturer) {
auto output = FakeAudioOutput::Create(&threading_model(), &device_registry_);
under_test_.AddOutput(output.get());
auto loopback_capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(loopback_capturer);
EXPECT_THAT(loopback_capturer->SourceLinks(), IsEmpty());
under_test_.SetLoopbackCapturerRoutingProfile(
loopback_capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
under_test_.SetLoopbackCapturerRoutingProfile(
loopback_capturer.get(),
{.routable = false, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(loopback_capturer->SourceLinks(), IsEmpty());
}
const audio_stream_unique_id_t kSupportsAllDeviceId = audio_stream_unique_id_t{.data = {0x33}};
const audio_stream_unique_id_t kUnconfiguredDeviceId = audio_stream_unique_id_t{.data = {0x45}};
static const RoutingConfig kConfigWithMediaExternalRoutingPolicy = RoutingConfig(
/*profiles=*/{{kSupportsAllDeviceId,
RoutingConfig::DeviceProfile(
/*eligible_for_loopback=*/true,
/*output_usage_support_set=*/
{fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::BACKGROUND),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::MEDIA),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::INTERRUPTION),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::SYSTEM_AGENT),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::COMMUNICATION)})}},
/*default=*/{RoutingConfig::DeviceProfile(
/*eligible_for_loopback=*/true, /*output_usage_support_set=*/{
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::MEDIA)})});
class RouteGraphWithMediaExternalPolicyTest : public RouteGraphTest {
public:
RouteGraphWithMediaExternalPolicyTest() : RouteGraphTest(kConfigWithMediaExternalRoutingPolicy) {}
};
TEST_F(RouteGraphWithMediaExternalPolicyTest, MediaRoutesToLastPluggedSupportedDevice) {
auto output_and_driver = OutputWithDeviceId(kSupportsAllDeviceId);
auto output = output_and_driver.output.get();
under_test_.AddOutput(output);
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::MEDIA)});
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({output}));
auto unconfigured_output_and_driver = OutputWithDeviceId(kUnconfiguredDeviceId);
auto unconfigured_output = unconfigured_output_and_driver.output.get();
under_test_.AddOutput(unconfigured_output);
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({unconfigured_output}));
}
TEST_F(RouteGraphWithMediaExternalPolicyTest, InterruptionDoesNotRouteToUnsupportedDevice) {
auto output_and_driver = OutputWithDeviceId(kSupportsAllDeviceId);
auto output = output_and_driver.output.get();
under_test_.AddOutput(output);
auto renderer = FakeAudioObject::FakeRenderer();
under_test_.AddRenderer(renderer);
under_test_.SetRendererRoutingProfile(
renderer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioRenderUsage::INTERRUPTION)});
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({output}));
auto unconfigured_output_and_driver = OutputWithDeviceId(kUnconfiguredDeviceId);
auto unconfigured_output = unconfigured_output_and_driver.output.get();
under_test_.AddOutput(unconfigured_output);
EXPECT_THAT(renderer->DestLinks(), UnorderedElementsAreArray({output}));
}
const audio_stream_unique_id_t kSupportsLoopbackDeviceId = audio_stream_unique_id_t{.data = {0x7a}};
static const RoutingConfig kConfigWithExternNonLoopbackDevicePolicy = RoutingConfig(
/*profiles=*/{{kSupportsAllDeviceId,
RoutingConfig::DeviceProfile(
/*eligible_for_loopback=*/true,
/*output_usage_support_set=*/
{fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::BACKGROUND),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::MEDIA),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::INTERRUPTION),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::SYSTEM_AGENT),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::COMMUNICATION)})},
{kSupportsLoopbackDeviceId,
RoutingConfig::DeviceProfile(
/*eligible_for_loopback=*/true,
/*output_usage_support_set=*/
{fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::BACKGROUND)})}},
/*default=*/{RoutingConfig::DeviceProfile(
/*eligible_for_loopback=*/false, /*output_usage_support_set=*/{
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::BACKGROUND),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::MEDIA),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::INTERRUPTION),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::SYSTEM_AGENT),
fidl::ToUnderlying(fuchsia::media::AudioRenderUsage::COMMUNICATION)})});
class RouteGraphWithExternalNonLoopbackDeviceTest : public RouteGraphTest {
public:
RouteGraphWithExternalNonLoopbackDeviceTest()
: RouteGraphTest(kConfigWithExternNonLoopbackDevicePolicy) {}
};
TEST_F(RouteGraphWithExternalNonLoopbackDeviceTest, LoopbackRoutesToLastPluggedSupported) {
auto output_and_driver = OutputWithDeviceId(kSupportsAllDeviceId);
auto output = output_and_driver.output.get();
under_test_.AddOutput(output);
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({output}));
auto second_output_and_driver = OutputWithDeviceId(kSupportsLoopbackDeviceId);
auto second_output = second_output_and_driver.output.get();
under_test_.AddOutput(second_output);
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({second_output}));
}
TEST_F(RouteGraphWithExternalNonLoopbackDeviceTest, LoopbackDoesNotRouteToUnsupportedDevice) {
auto output_and_driver = OutputWithDeviceId(kSupportsAllDeviceId);
auto output = output_and_driver.output.get();
under_test_.AddOutput(output);
auto capturer = FakeAudioObject::FakeCapturer();
under_test_.AddLoopbackCapturer(capturer);
under_test_.SetLoopbackCapturerRoutingProfile(
capturer.get(),
{.routable = true, .usage = UsageFrom(fuchsia::media::AudioCaptureUsage::SYSTEM_AGENT)});
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({output}));
auto second_output_and_driver = OutputWithDeviceId(kUnconfiguredDeviceId);
auto second_output = second_output_and_driver.output.get();
under_test_.AddOutput(second_output);
EXPECT_THAT(capturer->SourceLinks(), UnorderedElementsAreArray({output}));
}
} // namespace
} // namespace media::audio