| // 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/drivers/tests/basic_test.h" |
| |
| #include <fuchsia/hardware/audio/cpp/fidl.h> |
| #include <fuchsia/media/cpp/fidl.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/fidl/cpp/enum.h> |
| #include <lib/syslog/cpp/macros.h> |
| |
| #include <optional> |
| #include <string> |
| |
| #include <gtest/gtest.h> |
| |
| #include "src/media/audio/drivers/tests/test_base.h" |
| |
| namespace media::audio::drivers::test { |
| |
| constexpr bool kLogGainValues = false; |
| void LogGainState(std::string_view prologue, |
| const fuchsia::hardware::audio::GainState& gain_state) { |
| if constexpr (kLogGainValues) { |
| FX_LOGS(INFO) |
| << prologue |
| << (gain_state.has_gain_db() ? std::to_string(gain_state.gain_db()) : "UNSPECIFIED") |
| << " dB, muted is " |
| << (gain_state.has_muted() ? (gain_state.muted() ? "true" : "false") : "UNSPECIFIED") |
| << ", AGC is " |
| << (gain_state.has_agc_enabled() ? (gain_state.agc_enabled() ? "enabled" : "disabled") |
| : "UNSPECIFIED"); |
| } |
| } |
| |
| void BasicTest::TearDown() { |
| // Restore initial_gain_state_, if we changed the gain in this test case. |
| if (stream_config().is_bound() && initial_gain_state_ && set_gain_state_) { |
| LogGainState("Restoring previous gain: ", *initial_gain_state_); |
| stream_config()->SetGain(std::move(*initial_gain_state_)); |
| initial_gain_state_.reset(); |
| } |
| |
| TestBase::TearDown(); |
| } |
| |
| // Requests on protocols that are composesd into StreamConfig/Dai/Codec/Composite. |
| // |
| // fuchsia.hardware.audio.Health |
| // We expect a response, and we allow 'healthy' to be either unspecified or TRUE. |
| void BasicTest::RequestHealthAndExpectHealthy() { |
| GetHealthState(AddCallback("GetHealthState", [](fuchsia::hardware::audio::HealthState state) { |
| EXPECT_TRUE(!state.has_healthy() || state.healthy()); |
| })); |
| ExpectCallbacks(); |
| } |
| |
| void BasicTest::GetHealthState(fuchsia::hardware::audio::Health::GetHealthStateCallback cb) { |
| if (device_entry().isCodec()) { |
| codec()->GetHealthState(std::move(cb)); |
| } else if (device_entry().isComposite()) { |
| composite()->GetHealthState(std::move(cb)); |
| } else if (device_entry().isDai()) { |
| dai()->GetHealthState(std::move(cb)); |
| } else if (device_entry().isStreamConfig()) { |
| stream_config()->GetHealthState(std::move(cb)); |
| } |
| } |
| |
| // Basic (non-privileged) requests |
| // |
| // Request properties including unique ID (which should be unique across instances). |
| // The FIDL table for properties differs across Codec/Composite/Dai/StreamConfig. |
| // Extract these into a common struct so that subsequent code can be shared. |
| void BasicTest::RetrieveProperties() { |
| properties_.reset(); |
| // TODO(b/315049103): actually ensure that this differs between input and output. |
| if (device_entry().isCodec()) { |
| codec()->GetProperties(AddCallback( |
| "Codec::GetProperties", [this](fuchsia::hardware::audio::CodecProperties props) { |
| properties_ = BaseProperties{}; |
| if (props.has_is_input()) { |
| properties_->is_input = props.is_input(); |
| } |
| if (props.has_unique_id()) { |
| properties_->unique_id.emplace(); |
| std::memcpy(properties_->unique_id->data(), props.unique_id().data(), 16); |
| } |
| if (props.has_manufacturer()) { |
| properties_->manufacturer = props.manufacturer(); |
| } |
| if (props.has_product()) { |
| properties_->product = props.product(); |
| } |
| if (props.has_plug_detect_capabilities()) { |
| properties_->plug_detect_capabilities = props.plug_detect_capabilities(); |
| } |
| })); |
| } else if (device_entry().isComposite()) { |
| composite()->GetProperties(AddCallback( |
| "Composite::GetProperties", [this](fuchsia::hardware::audio::CompositeProperties props) { |
| properties_ = BaseProperties{}; |
| if (props.has_unique_id()) { |
| properties_->unique_id.emplace(props.unique_id()); |
| } |
| if (props.has_manufacturer()) { |
| properties_->manufacturer = props.manufacturer(); |
| } |
| if (props.has_product()) { |
| properties_->product = props.product(); |
| } |
| if (props.has_clock_domain()) { |
| properties_->clock_domain = props.clock_domain(); |
| } |
| })); |
| } else if (device_entry().isDai()) { |
| dai()->GetProperties( |
| AddCallback("Dai::GetProperties", [this](fuchsia::hardware::audio::DaiProperties props) { |
| properties_ = BaseProperties{}; |
| if (props.has_is_input()) { |
| properties_->is_input = props.is_input(); |
| } |
| if (props.has_unique_id()) { |
| properties_->unique_id = props.unique_id(); |
| } |
| if (props.has_manufacturer()) { |
| properties_->manufacturer = props.manufacturer(); |
| } |
| if (props.has_product_name()) { // Note: not 'product' |
| properties_->product = props.product_name(); |
| } |
| if (props.has_clock_domain()) { |
| properties_->clock_domain = props.clock_domain(); |
| } |
| })); |
| } else if (device_entry().isStreamConfig()) { |
| stream_config()->GetProperties(AddCallback( |
| "StreamConfig::GetProperties", [this](fuchsia::hardware::audio::StreamProperties props) { |
| properties_ = BaseProperties{}; |
| if (props.has_is_input()) { |
| properties_->is_input = props.is_input(); |
| } |
| if (props.has_unique_id()) { |
| properties_->unique_id = props.unique_id(); |
| } |
| if (props.has_manufacturer()) { |
| properties_->manufacturer = props.manufacturer(); |
| } |
| if (props.has_product()) { |
| properties_->product = props.product(); |
| } |
| if (props.has_clock_domain()) { |
| properties_->clock_domain = props.clock_domain(); |
| } |
| if (props.has_plug_detect_capabilities()) { |
| properties_->plug_detect_capabilities = props.plug_detect_capabilities(); |
| } |
| if (props.has_can_mute()) { |
| properties_->can_mute = props.can_mute(); |
| } |
| if (props.has_can_agc()) { |
| properties_->can_agc = props.can_agc(); |
| } |
| if (props.has_min_gain_db()) { |
| properties_->min_gain_db = props.min_gain_db(); |
| } |
| if (props.has_max_gain_db()) { |
| properties_->max_gain_db = props.max_gain_db(); |
| } |
| if (props.has_gain_step_db()) { |
| properties_->gain_step_db = props.gain_step_db(); |
| } |
| })); |
| } |
| ExpectCallbacks(); |
| EXPECT_TRUE(properties_.has_value()) << "No GetProperties completion was received"; |
| } |
| |
| // For debugging purposes |
| void BasicTest::DisplayBaseProperties() { |
| ASSERT_TRUE(properties_); |
| |
| FX_LOGS(INFO) << driver_type() << " is_input: " |
| << (properties_->is_input.has_value() ? std::to_string(*properties_->is_input) |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " manufacturer is " |
| << (properties_->manufacturer.has_value() ? "'" + *properties_->manufacturer + "'" |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " product is " |
| << (properties_->product.has_value() ? "'" + *properties_->product + "'" : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " unique_id is " << properties_->unique_id; |
| FX_LOGS(INFO) << driver_type() << " clock domain is " |
| << (properties_->clock_domain.has_value() |
| ? std::to_string(*properties_->clock_domain) |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " plug_detect is " << properties_->plug_detect_capabilities; |
| FX_LOGS(INFO) << driver_type() << " min_gain_db is " |
| << (properties_->min_gain_db.has_value() ? std::to_string(*properties_->min_gain_db) |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " max_gain_db is " |
| << (properties_->max_gain_db.has_value() ? std::to_string(*properties_->max_gain_db) |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " gain_step_db is " |
| << (properties_->gain_step_db.has_value() |
| ? std::to_string(*properties_->gain_step_db) |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " can_mute is " |
| << (properties_->can_mute.has_value() ? std::to_string(*properties_->can_mute) |
| : "NONE"); |
| FX_LOGS(INFO) << driver_type() << " can_agc is " |
| << (properties_->can_agc.has_value() ? std::to_string(*properties_->can_agc) |
| : "NONE"); |
| } |
| |
| void BasicTest::ValidateProperties() { |
| ASSERT_TRUE(properties_); |
| |
| // The following fields are optional, but must be non-empty if they are specified. |
| EXPECT_FALSE(properties_->manufacturer.has_value() && properties_->manufacturer->empty()); |
| EXPECT_FALSE(properties_->product.has_value() && properties_->product->empty()); |
| |
| // Just check that required fields are present |
| if (device_entry().isCodec()) { |
| EXPECT_TRUE(properties_->plug_detect_capabilities.has_value()); |
| } else if (device_entry().isComposite()) { |
| EXPECT_TRUE(properties_->clock_domain.has_value()); |
| } else if (device_entry().isDai()) { |
| EXPECT_TRUE(properties_->is_input.has_value()); |
| EXPECT_TRUE(properties_->clock_domain.has_value()); |
| } else if (device_entry().isStreamConfig()) { |
| ASSERT_TRUE(properties_->is_input.has_value()); |
| EXPECT_TRUE(properties_->clock_domain.has_value()); |
| EXPECT_TRUE(properties_->plug_detect_capabilities.has_value()); |
| ASSERT_TRUE(properties_->min_gain_db.has_value()); |
| ASSERT_TRUE(properties_->max_gain_db.has_value()); |
| ASSERT_TRUE(properties_->gain_step_db.has_value()); |
| |
| // For StreamConfig, we can do additional data validity/range checks. |
| EXPECT_EQ(*properties_->is_input, driver_type() == DriverType::StreamConfigInput); |
| ASSERT_TRUE(std::isfinite(*properties_->min_gain_db)) << "irregular min_gain_db"; |
| ASSERT_TRUE(std::isfinite(*properties_->max_gain_db)) << "irregular max_gain_db"; |
| ASSERT_TRUE(std::isfinite(*properties_->gain_step_db)) << "irregular gain_step_db"; |
| EXPECT_LE(*properties_->min_gain_db, *properties_->max_gain_db) << "max_gain_db too small"; |
| EXPECT_GE(*properties_->gain_step_db, 0.0f) << "gain_step_db too small"; |
| EXPECT_LE(*properties_->gain_step_db, *properties_->max_gain_db - *properties_->min_gain_db) |
| << "gain_step_db too large"; |
| } else { |
| FAIL() << "Unknown device type"; |
| } |
| } |
| |
| // Request that the driver return its gain capabilities and current state, expecting a response. |
| // TODO(b/315051281): If possible, combine this with the corresponding check of the signalprocessing |
| // gain element, once that test exists. |
| void BasicTest::WatchGainStateAndExpectUpdate() { |
| ASSERT_TRUE(properties_); |
| ASSERT_TRUE(device_entry().isStreamConfig()); |
| |
| // We reconnect the stream every time we run a test, and by driver interface definition the driver |
| // must reply to the first watch request, so we get gain state by issuing a watch FIDL call. |
| stream_config()->WatchGainState( |
| AddCallback("WatchGainState", [this](fuchsia::hardware::audio::GainState gain_state) { |
| LogGainState((initial_gain_state_ ? "Received gain update: " : "Storing previous gain: "), |
| gain_state); |
| |
| ASSERT_TRUE(gain_state.has_gain_db()); |
| EXPECT_GE(gain_state.gain_db(), *properties_->min_gain_db); |
| EXPECT_LE(gain_state.gain_db(), *properties_->max_gain_db); |
| |
| // If we're muted, then we must be capable of muting. |
| EXPECT_TRUE(!gain_state.has_muted() || !gain_state.muted() || *properties_->can_mute); |
| // If AGC is enabled, then we must be capable of AGC. |
| EXPECT_TRUE(!gain_state.has_agc_enabled() || !gain_state.agc_enabled() || |
| *properties_->can_agc); |
| if (!initial_gain_state_.has_value()) { |
| initial_gain_state_ = std::move(gain_state); |
| } |
| })); |
| ExpectCallbacks(); |
| } |
| |
| // Request that the driver return its current gain state, expecting no response (no change). |
| // TODO(b/315051281): If possible, combine this with the corresponding check of the signalprocessing |
| // gain element, once that test exists. |
| void BasicTest::WatchGainStateAndExpectNoUpdate() { |
| ASSERT_TRUE(device_entry().isStreamConfig()); |
| |
| stream_config()->WatchGainState([](fuchsia::hardware::audio::GainState gain_state) { |
| FAIL() << "Unexpected gain update received"; |
| }); |
| } |
| |
| // Determine an appropriate gain state to request, then call other method to request that driver set |
| // gain. This method assumes that the driver already successfully responded to a GetInitialGainState |
| // request. If this device's gain is fixed and cannot be changed, then SKIP the test. |
| // TODO(b/315051281): If possible, combine this with the corresponding check of the signalprocessing |
| // gain element, once that test exists. |
| void BasicTest::RequestSetGain() { |
| ASSERT_TRUE(device_entry().isStreamConfig()) << __func__ << ": device_entry is not StreamConfig"; |
| ASSERT_TRUE(properties_); |
| if (*properties_->max_gain_db == *properties_->min_gain_db && !*properties_->can_mute && |
| !*properties_->can_agc) { |
| GTEST_SKIP() << "*** Audio " << driver_type() << " has fixed gain (" |
| << initial_gain_state_->gain_db() |
| << " dB) and cannot MUTE or AGC. Skipping SetGain test. ***"; |
| } |
| |
| // Ensure we've retrieved initial gain settings, so we can restore them after this test case. |
| ASSERT_TRUE(initial_gain_state_); |
| |
| // Base our new gain settings on the old ones, to avoid existing values. |
| fuchsia::hardware::audio::GainState gain_state_to_set; |
| ASSERT_EQ(initial_gain_state_->Clone(&gain_state_to_set), ZX_OK); |
| // Base our new gain settings on the old ones: avoid existing values so this Set is a change. |
| // If we got this far, we know we can change something (even if it isn't gain_db). |
| // Change to a different gain_db. |
| *gain_state_to_set.mutable_gain_db() = |
| (initial_gain_state_->gain_db() == *properties_->min_gain_db ? *properties_->max_gain_db |
| : *properties_->min_gain_db); |
| // Toggle muted if we can change it (explicitly set it to false, if we can't). |
| *gain_state_to_set.mutable_muted() = |
| *properties_->can_mute && !(gain_state_to_set.has_muted() && gain_state_to_set.muted()); |
| // Toggle AGC if we can change it (explicitly set it to false, if we can't). |
| *gain_state_to_set.mutable_agc_enabled() = |
| *properties_->can_agc && |
| !(gain_state_to_set.has_agc_enabled() && gain_state_to_set.agc_enabled()); |
| |
| set_gain_state_ = true; |
| LogGainState("SetGain about to set: ", gain_state_to_set); |
| stream_config()->SetGain(std::move(gain_state_to_set)); |
| } |
| |
| // TODO(b/315051014): If possible, combine this with the corresponding plug check of the |
| // signalprocessing endpoint element, once that test exists. |
| void BasicTest::ValidatePlugState(const fuchsia::hardware::audio::PlugState& plug_state) { |
| ASSERT_TRUE(plug_state.has_plugged()); |
| if (!plug_state.plugged()) { |
| ASSERT_TRUE(properties_); |
| ASSERT_TRUE(properties_->plug_detect_capabilities.has_value()); |
| EXPECT_NE(*properties_->plug_detect_capabilities, |
| fuchsia::hardware::audio::PlugDetectCapabilities::HARDWIRED) |
| << "Device reported plug capabilities as HARDWIRED, but now reports as unplugged"; |
| } |
| |
| EXPECT_TRUE(plug_state.has_plug_state_time()); |
| EXPECT_GE(plug_state.plug_state_time(), 0u); |
| EXPECT_LT(plug_state.plug_state_time(), zx::clock::get_monotonic().get()); |
| } |
| |
| // Request that the driver return its current plug state, expecting a valid response. |
| // TODO(b/315051014): If possible, combine this with the corresponding plug check of the |
| // signalprocessing endpoint element, once that test exists. |
| void BasicTest::WatchPlugStateAndExpectUpdate() { |
| ASSERT_TRUE(properties_); |
| |
| // Since we reconnect to the audio stream every time we run this test and we are guaranteed by |
| // the audio driver interface definition that the driver will reply to the first watch request, |
| // we can get the plug state by issuing a watch FIDL call. |
| fuchsia::hardware::audio::PlugState initial_plug_state; |
| if (device_entry().isCodec()) { |
| codec()->WatchPlugState(AddCallback( |
| "Codec::WatchPlugState", [&initial_plug_state](fuchsia::hardware::audio::PlugState state) { |
| initial_plug_state = std::move(state); |
| })); |
| } else if (device_entry().isStreamConfig()) { |
| stream_config()->WatchPlugState( |
| AddCallback("StreamConfig::WatchPlugState", |
| [&initial_plug_state](fuchsia::hardware::audio::PlugState state) { |
| initial_plug_state = std::move(state); |
| })); |
| } else { |
| FAIL() << "Wrong device type for " << __func__; |
| } |
| ExpectCallbacks(); |
| if (!HasFailure()) { |
| ValidatePlugState(initial_plug_state); |
| } |
| } |
| |
| // Request that the driver return its current plug state, expecting no response (no change). |
| // TODO(b/315051014): If possible, combine this with the corresponding plug check of the |
| // signalprocessing endpoint element, once that test exists. |
| void BasicTest::WatchPlugStateAndExpectNoUpdate() { |
| if (device_entry().isCodec()) { |
| codec()->WatchPlugState([](fuchsia::hardware::audio::PlugState state) { |
| FAIL() << "Codec::WatchPlugState: unexpected plug update received"; |
| }); |
| } else if (device_entry().isStreamConfig()) { |
| stream_config()->WatchPlugState([](fuchsia::hardware::audio::PlugState state) { |
| FAIL() << "StreamConfig::WatchPlugState: unexpected plug update received"; |
| }); |
| } else { |
| FAIL() << "Wrong device type for " << __func__; |
| } |
| } |
| |
| #define DEFINE_BASIC_TEST_CLASS(CLASS_NAME, CODE) \ |
| class CLASS_NAME : public BasicTest { \ |
| public: \ |
| explicit CLASS_NAME(const DeviceEntry& dev_entry) : BasicTest(dev_entry) {} \ |
| void TestBody() override { CODE } \ |
| } |
| |
| // Test cases that target each of the various Stream channel commands |
| |
| // Verify the driver responds to the GetHealthState query. |
| DEFINE_BASIC_TEST_CLASS(Health, { RequestHealthAndExpectHealthy(); }); |
| |
| // Verify a valid unique_id, manufacturer, product and gain capabilities is successfully received. |
| DEFINE_BASIC_TEST_CLASS(GetProperties, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| ValidateProperties(); |
| }); |
| |
| // Verify the initial WatchGainState responses are successfully received. |
| DEFINE_BASIC_TEST_CLASS(GetInitialGainState, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| |
| WatchGainStateAndExpectUpdate(); |
| WaitForError(); |
| }); |
| |
| // Verify that no response is received, for a subsequent WatchGainState request. |
| DEFINE_BASIC_TEST_CLASS(WatchGainSecondTimeNoResponse, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| ASSERT_NO_FAILURE_OR_SKIP(WatchGainStateAndExpectUpdate()); |
| |
| WatchGainStateAndExpectNoUpdate(); |
| WaitForError(); |
| }); |
| |
| // Verify valid set gain responses are successfully received. |
| DEFINE_BASIC_TEST_CLASS(SetGain, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| ASSERT_NO_FAILURE_OR_SKIP(WatchGainStateAndExpectUpdate()); |
| |
| RequestSetGain(); |
| WaitForError(); |
| }); |
| |
| // Verify that format-retrieval responses are successfully received and are complete and valid. |
| DEFINE_BASIC_TEST_CLASS(RingBufferFormats, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveRingBufferFormats()); |
| WaitForError(); |
| }); |
| |
| // Verify that format-retrieval responses are successfully received and are complete and valid. |
| DEFINE_BASIC_TEST_CLASS(DaiFormats, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveDaiFormats()); |
| WaitForError(); |
| }); |
| |
| // Verify that a valid initial plug detect response is successfully received. |
| DEFINE_BASIC_TEST_CLASS(GetInitialPlugState, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| |
| WatchPlugStateAndExpectUpdate(); |
| WaitForError(); |
| |
| // Someday: determine how to trigger the driver's internal hardware-detect mechanism, so it |
| // emits unsolicited PLUG/UNPLUG events -- otherwise driver plug detect updates are not fully |
| // testable. |
| }); |
| |
| // Verify that no response is received, for a subsequent WatchPlugState request. |
| DEFINE_BASIC_TEST_CLASS(WatchPlugSecondTimeNoResponse, { |
| ASSERT_NO_FAILURE_OR_SKIP(RetrieveProperties()); |
| ASSERT_NO_FAILURE_OR_SKIP(WatchPlugStateAndExpectUpdate()); |
| |
| WatchPlugStateAndExpectNoUpdate(); |
| WaitForError(); |
| }); |
| |
| // Verify that a valid toplogy is successfully received. |
| DEFINE_BASIC_TEST_CLASS(Topology, { RequestTopologies(); }); |
| |
| // Register separate test case instances for each enumerated device |
| // |
| // See googletest/docs/advanced.md for details |
| #define REGISTER_BASIC_TEST(CLASS_NAME, DEVICE) \ |
| { \ |
| testing::RegisterTest("BasicTest", TestNameForEntry(#CLASS_NAME, DEVICE).c_str(), nullptr, \ |
| DevNameForEntry(DEVICE).c_str(), __FILE__, __LINE__, \ |
| [&]() -> BasicTest* { return new CLASS_NAME(DEVICE); }); \ |
| } |
| |
| void RegisterBasicTestsForDevice(const DeviceEntry& device_entry) { |
| if (device_entry.isCodec()) { |
| REGISTER_BASIC_TEST(Health, device_entry); |
| REGISTER_BASIC_TEST(GetProperties, device_entry); |
| REGISTER_BASIC_TEST(DaiFormats, device_entry); |
| REGISTER_BASIC_TEST(GetInitialPlugState, device_entry); |
| REGISTER_BASIC_TEST(WatchPlugSecondTimeNoResponse, device_entry); |
| } else if (device_entry.isComposite()) { |
| REGISTER_BASIC_TEST(Health, device_entry); |
| REGISTER_BASIC_TEST(Topology, device_entry); |
| REGISTER_BASIC_TEST(GetProperties, device_entry); |
| REGISTER_BASIC_TEST(RingBufferFormats, device_entry); |
| REGISTER_BASIC_TEST(DaiFormats, device_entry); |
| } else if (device_entry.isDai()) { |
| REGISTER_BASIC_TEST(Health, device_entry); |
| REGISTER_BASIC_TEST(GetProperties, device_entry); |
| REGISTER_BASIC_TEST(RingBufferFormats, device_entry); |
| REGISTER_BASIC_TEST(DaiFormats, device_entry); |
| } else if (device_entry.isStreamConfig()) { |
| REGISTER_BASIC_TEST(Health, device_entry); |
| REGISTER_BASIC_TEST(GetProperties, device_entry); |
| REGISTER_BASIC_TEST(GetInitialGainState, device_entry); |
| REGISTER_BASIC_TEST(WatchGainSecondTimeNoResponse, device_entry); |
| REGISTER_BASIC_TEST(SetGain, device_entry); |
| REGISTER_BASIC_TEST(RingBufferFormats, device_entry); |
| REGISTER_BASIC_TEST(GetInitialPlugState, device_entry); |
| REGISTER_BASIC_TEST(WatchPlugSecondTimeNoResponse, device_entry); |
| } else { |
| FAIL() << "Unknown device type for entry '" << device_entry.filename << "'"; |
| } |
| } |
| |
| // TODO(https://fxbug.dev/42075676): Add testing for Composite protocol methods. |
| |
| // TODO(b/302704556): Add tests for Watch-while-still-pending (specifically WatchGainState, |
| // WatchPlugState, WatchClockRecoveryPositionInfo, WatchDelayInfo, WatchElementState and |
| // WatchTopology). |
| |
| // TODO(https://fxbug.dev/42077405): Add testing for SignalProcessing methods. |
| // |
| // Proposed test cases for fuchsia.hardware.audio.signalprocessing listed below: |
| // BasicTest cases: |
| // SignalProcessingSupport |
| // SignalProcessingConnector::SignalProcessingConnect returns and does not close channel. |
| // child protocol channel stays bound if supported, and closes with ZX_ERR_NOT_SUPPORTED if |
| // not. |
| // SignalProcessingElements |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If GetElements closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // GetElements returns a vector with [1,64] entries. |
| // Implies that GetTopologies must return a non-empty vector. |
| // For each element: |
| // id and type are required. |
| // ElementType matches the TypeSpecificElement. |
| // Save the elements in a set, for recognition in later cases. |
| // SignalProcessingTopologies |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If GetTopologies closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // GetTopologies returns a vector with [1,64] entries |
| // Implies that GetElements must return a non-empty vector. |
| // WatchTopology returns a value that is in the range returned by GetTopologies. |
| // For each topology element: |
| // id and processing_elements_edge_pairs are required. |
| // For each processing_elements_edge_pairs entry: |
| // processing_element_id_from and processing_element_id_to are both known (in elements |
| // set). |
| // WatchTopologyWhilePending |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If GetTopologies closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // GetTopologies returns a vector with [1,64] entries |
| // WatchTopology returns a value that is in the range returned by GetTopologies. |
| // WatchTopology (again) closes the protocol channel with ZX_ERR_BAD_STATE |
| // InitialElementState |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If WatchElementState closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other |
| // error. Else set a static var for this driver instance that SignalProcessing is supported. |
| // WatchElementState immediately returns when initially called. |
| // Callback contains a valid complete ElementState that matches the ElementType. |
| // WatchElementStateBadId |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve elements. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // WatchElementState(badId) returns ZX_ERR_INVALID_ARGS and does not close. Fail on other |
| // error. |
| // WatchElementStateWhilePending |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If WatchElementState closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other |
| // error. Else set a static var for this driver instance that SignalProcessing is supported. |
| // WatchElementState immediately returns when initially called. |
| // WatchElementState (again) closes the protocol channel with ZX_ERR_BAD_STATE |
| |
| // AdminTest cases: |
| // SetTopologySupported |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve topologies. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // SetTopology returns callback. |
| // WatchTopology acknowledges the change made by SetTopology. |
| // SetTopologyBadId |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve topologies. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // SetTopology(badId) returns ZX_ERR_INVALID_ARGS, does not close channel. Fail on other error. |
| // WatchTopology does not return. |
| // SetTopologyInvalidated |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve topologies. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // First make a change that invalidates the SignalProcessing configuration, then |
| // WatchTopology should ... return ZX_ERR_BAD_STATE and not close channel? |
| // SetTopology should return ZX_ERR_BAD_STATE and not close channel. |
| // SetTopologyReconfigured |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // First invalidate the SignalProcessing configuration, then retrieve the new topologies. |
| // SetTopology returns callback (does not fail or close channel). |
| // WatchTopology acknowledges the change made by SetTopology. |
| // SetElementState |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If SetElementState closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // SetElementState returns callback. Any other observable state? |
| // SetElementStateElementSpecific |
| // Detailed checks of specific input or output fields that are unique to the element type. |
| // ... likely multiple test cases here, one for each ElementType |
| // SetElementStateNoChange |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // If SetElementState closes channel with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // SetElementState does not returns callback, trigger WatchElementStateChange or close channel. |
| // SetElementStateBadId |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve elements. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // SetElementState(badId) returns ZX_ERR_INVALID_ARGS, not close channel. Fail on other error. |
| // SetElementStateBadValues |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve elements. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // SetElementState(badVal) returns ZX_ERR_INVALID_ARGS, not close channel. Fail on other error. |
| // SetElementStateInvalidated |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve elements. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // First make a change that invalidates the SignalProcessing configuration, then |
| // SetElementState should return ZX_ERR_BAD_STATE and not close channel. |
| // SetElementStateReconfigured |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // First invalidate the SignalProcessing configuration, then retrieve new elements/topologies. |
| // SetElementState returns callback (does not fail or close channel). |
| // WatchElementStateChange |
| // If SignalProcessingConnect not supported earlier, SKIP. |
| // Retrieve elements. If closes with ZX_ERR_NOT_SUPPORTED, SKIP. Fail on any other error. |
| // Else set a static var for this driver instance that SignalProcessing is supported. |
| // WatchElementState pends until SetElementState is called. |
| // Upon change, returns callback with values that match SetElementState. |
| |
| } // namespace media::audio::drivers::test |