| // Copyright 2020 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/activity_dispatcher.h" |
| |
| #include <lib/fidl/cpp/binding.h> |
| #include <lib/gtest/test_loop_fixture.h> |
| |
| #include <gtest/gtest.h> |
| |
| namespace media::audio { |
| |
| using Activity = ActivityDispatcherImpl::Activity; |
| using UsageVector = std::vector<fuchsia::media::AudioRenderUsage>; |
| |
| namespace { |
| Activity UsageVectorToActivity(const std::vector<fuchsia::media::AudioRenderUsage>& usage_vector) { |
| Activity activity; |
| for (const auto& usage : usage_vector) { |
| activity.set(static_cast<int>(usage)); |
| } |
| return activity; |
| } |
| } // namespace |
| |
| class ActivityDispatcherTest : public gtest::TestLoopFixture { |
| protected: |
| // Simulates a consumer connecting to the dispatcher. |
| fuchsia::media::ActivityReporterPtr GetClient() { |
| fuchsia::media::ActivityReporterPtr activity_reporter; |
| activity_dispatcher_.GetFidlRequestHandler()(activity_reporter.NewRequest()); |
| return activity_reporter; |
| } |
| |
| // Simulates a new set of usages being active. |
| void UpdateActivity(const UsageVector& usage_vector) { |
| activity_dispatcher_.OnActivityChanged(UsageVectorToActivity(usage_vector)); |
| } |
| |
| private: |
| ActivityDispatcherImpl activity_dispatcher_; |
| }; |
| |
| TEST_F(ActivityDispatcherTest, FirstWatchReturnsImmediately) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { called = true; }); |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(called); |
| } |
| |
| TEST_F(ActivityDispatcherTest, SecondWatchHangsWithoutUpdate) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool first_called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { first_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(first_called); |
| |
| // Check that the Watch does not return without an update in the activity. |
| bool second_called = false; |
| activity_reporter->WatchRenderActivity( |
| [&](const UsageVector& activity) { second_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_FALSE(second_called); |
| } |
| |
| TEST_F(ActivityDispatcherTest, SecondWatchReturnsWithUpdate) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool first_called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { first_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(first_called); |
| |
| bool second_called = false; |
| UsageVector actual_usages; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& usages) { |
| second_called = true; |
| actual_usages = usages; |
| }); |
| RunLoopUntilIdle(); |
| EXPECT_FALSE(second_called); |
| |
| UsageVector expected_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND}; |
| UpdateActivity(expected_usages); |
| |
| // Check that the Watch does return with an update in the activity. |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(second_called); |
| EXPECT_EQ(expected_usages, actual_usages); |
| } |
| |
| TEST_F(ActivityDispatcherTest, WatchReturnsCachedValue) { |
| UsageVector expected_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND}; |
| UpdateActivity(expected_usages); |
| RunLoopUntilIdle(); |
| |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool called = false; |
| UsageVector actual_usages; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& usages) { |
| called = true; |
| actual_usages = usages; |
| }); |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(called); |
| EXPECT_EQ(expected_usages, actual_usages); |
| } |
| |
| TEST_F(ActivityDispatcherTest, WatchSkipsTransientValue) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool first_called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { first_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(first_called); |
| |
| UsageVector transient_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND}; |
| UpdateActivity(transient_usages); |
| RunLoopUntilIdle(); |
| |
| UsageVector expected_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND, |
| fuchsia::media::AudioRenderUsage::SYSTEM_AGENT}; |
| UpdateActivity(expected_usages); |
| RunLoopUntilIdle(); |
| |
| // Check that the Watch returns the latest value and not the transient one. |
| bool second_called = false; |
| UsageVector actual_usages; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& usages) { |
| second_called = true; |
| actual_usages = usages; |
| }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(second_called); |
| EXPECT_EQ(expected_usages, actual_usages); |
| } |
| |
| TEST_F(ActivityDispatcherTest, WatchHangsAfterFlap) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool first_called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { first_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(first_called); |
| |
| UsageVector transient_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND}; |
| UpdateActivity(transient_usages); |
| RunLoopUntilIdle(); |
| |
| UsageVector original_usages = {}; |
| UpdateActivity(original_usages); |
| RunLoopUntilIdle(); |
| |
| // Check that the Watch does not return if original activity is restored before next Watch. |
| bool second_called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& usages) { second_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_FALSE(second_called); |
| } |
| |
| TEST_F(ActivityDispatcherTest, WatchHangsOnRedundantChange) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool first_called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { first_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(first_called); |
| |
| UsageVector redundant_usages = {}; |
| UpdateActivity(redundant_usages); |
| RunLoopUntilIdle(); |
| |
| // Check that redundant changes are not dispatched. |
| bool second_called = false; |
| UsageVector actual_usages; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& usages) { second_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_FALSE(second_called); |
| } |
| |
| TEST_F(ActivityDispatcherTest, MultipleClients) { |
| fuchsia::media::ActivityReporterPtr client = GetClient(); |
| |
| // First client gets first activity. |
| bool first_called = false; |
| client->WatchRenderActivity([&](const UsageVector& activity) { first_called = true; }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(first_called); |
| |
| UsageVector expected_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND}; |
| UpdateActivity(expected_usages); |
| RunLoopUntilIdle(); |
| |
| // First client gets second activity. |
| bool second_called = false; |
| UsageVector actual_usages; |
| client->WatchRenderActivity([&](const UsageVector& usages) { |
| second_called = true; |
| actual_usages = usages; |
| }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(second_called); |
| EXPECT_EQ(expected_usages, actual_usages); |
| |
| fuchsia::media::ActivityReporterPtr other_client = GetClient(); |
| |
| // Second client gets second activty. |
| bool third_called = false; |
| UsageVector other_actual_usages; |
| other_client->WatchRenderActivity([&](const UsageVector& usages) { |
| third_called = true; |
| other_actual_usages = usages; |
| }); |
| RunLoopUntilIdle(); |
| EXPECT_TRUE(third_called); |
| EXPECT_EQ(expected_usages, other_actual_usages); |
| |
| // Both client get the activity update. |
| bool first_client_called = false; |
| bool second_client_called = false; |
| UsageVector first_client_new_actual_usages; |
| UsageVector second_client_new_actual_usages; |
| client->WatchRenderActivity([&](const UsageVector& usages) { |
| first_client_called = true; |
| first_client_new_actual_usages = usages; |
| }); |
| other_client->WatchRenderActivity([&](const UsageVector& usages) { |
| second_client_called = true; |
| second_client_new_actual_usages = usages; |
| }); |
| RunLoopUntilIdle(); |
| |
| UsageVector new_expected_usages = {fuchsia::media::AudioRenderUsage::BACKGROUND, |
| fuchsia::media::AudioRenderUsage::SYSTEM_AGENT}; |
| UpdateActivity(new_expected_usages); |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(first_client_called); |
| EXPECT_TRUE(second_client_called); |
| EXPECT_EQ(new_expected_usages, first_client_new_actual_usages); |
| EXPECT_EQ(new_expected_usages, second_client_new_actual_usages); |
| } |
| |
| TEST_F(ActivityDispatcherTest, TwoHangingGetsTriggerError) { |
| fuchsia::media::ActivityReporterPtr activity_reporter = GetClient(); |
| |
| bool client_error_handler_invoked = false; |
| zx_status_t client_error_handler_status = ZX_OK; |
| activity_reporter.set_error_handler([&](zx_status_t status) { |
| client_error_handler_status = status; |
| client_error_handler_invoked = true; |
| }); |
| |
| bool called = false; |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) { called = true; }); |
| RunLoopUntilIdle(); |
| |
| EXPECT_TRUE(called); |
| |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) {}); |
| activity_reporter->WatchRenderActivity([&](const UsageVector& activity) {}); |
| RunLoopUntilIdle(); |
| |
| ASSERT_TRUE(client_error_handler_invoked); |
| EXPECT_EQ(client_error_handler_status, ZX_ERR_PEER_CLOSED); |
| } |
| } // namespace media::audio |