blob: 27708fac2c06166f7d461e0c0ddb296bc81ca211 [file]
// 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/test/basic_test.h"
#include <fuchsia/hardware/audio/cpp/fidl.h>
#include <lib/fdio/fdio.h>
#include <algorithm>
#include <cstring>
#include "src/media/audio/lib/logging/logging.h"
#include "src/media/audio/lib/test/message_transceiver.h"
namespace media::audio::drivers::test {
void BasicTest::SetUp() {
TestBase::SetUp();
// If previous test in this group found no device, we don't need to search again - skip this test.
if (no_devices_found()) {
GTEST_SKIP();
}
EnumerateDevices();
}
// Stream channel requests
//
// Request the stream properties including the driver's unique ID which must be unique between input
// and output.
// TODO(mpuryear): ensure that this differs between input and output.
void BasicTest::RequestStreamProperties() {
stream_config()->GetProperties([this](fuchsia::hardware::audio::StreamProperties prop) {
stream_props_ = std::move(prop);
if (stream_props_.has_unique_id()) {
char id_buf[2 * kUniqueIdLength + 1];
std::snprintf(id_buf, sizeof(id_buf),
"%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
stream_props_.unique_id()[0], stream_props_.unique_id()[1],
stream_props_.unique_id()[2], stream_props_.unique_id()[3],
stream_props_.unique_id()[4], stream_props_.unique_id()[5],
stream_props_.unique_id()[6], stream_props_.unique_id()[7],
stream_props_.unique_id()[8], stream_props_.unique_id()[9],
stream_props_.unique_id()[10], stream_props_.unique_id()[11],
stream_props_.unique_id()[12], stream_props_.unique_id()[13],
stream_props_.unique_id()[14], stream_props_.unique_id()[15]);
AUDIO_LOG(DEBUG) << "Received unique_id " << id_buf;
}
ASSERT_TRUE(stream_props_.has_is_input());
if (device_type() == DeviceType::Input) {
ASSERT_TRUE(prop.is_input());
} else {
ASSERT_FALSE(prop.is_input());
}
if (stream_props_.has_can_mute()) {
*stream_props_.mutable_can_mute() = false;
}
if (stream_props_.has_can_agc()) {
*stream_props_.mutable_can_agc() = false;
}
ASSERT_TRUE(stream_props_.has_min_gain_db());
ASSERT_TRUE(stream_props_.has_max_gain_db());
ASSERT_TRUE(stream_props_.has_gain_step_db());
ASSERT_TRUE(stream_props_.min_gain_db() <= stream_props_.max_gain_db());
ASSERT_TRUE(stream_props_.gain_step_db() >= 0);
if (stream_props_.max_gain_db() > stream_props_.min_gain_db()) {
EXPECT_GE(stream_props_.gain_step_db(), 0.0f);
} else {
EXPECT_EQ(stream_props_.gain_step_db(), 0.0f);
}
ASSERT_TRUE(stream_props_.has_plug_detect_capabilities());
if (stream_props_.has_manufacturer()) {
AUDIO_LOG(DEBUG) << "Received manufacturer " << stream_props_.manufacturer();
}
if (stream_props_.has_product()) {
AUDIO_LOG(DEBUG) << "Received product " << stream_props_.product();
}
ASSERT_TRUE(stream_props_.has_clock_domain());
received_get_stream_properties_ = true;
});
RunLoopUntil([this]() { return received_get_stream_properties_; });
}
// Request that the driver return its gain capabilities and current state.
void BasicTest::RequestGain() {
// 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 gain state by issuing a watch FIDL call.
stream_config()->WatchGainState([this](fuchsia::hardware::audio::GainState gain_state) {
AUDIO_LOG(DEBUG) << "Received gain " << gain_state.gain_db();
gain_state_ = std::move(gain_state);
if (!gain_state_.has_muted()) {
*gain_state_.mutable_muted() = false;
}
if (!gain_state_.has_agc_enabled()) {
*gain_state_.mutable_agc_enabled() = false;
}
EXPECT_TRUE(gain_state_.has_gain_db());
if (gain_state_.muted()) {
EXPECT_TRUE(stream_props_.can_mute());
}
if (gain_state_.agc_enabled()) {
EXPECT_TRUE(stream_props_.can_agc());
}
EXPECT_GE(gain_state_.gain_db(), stream_props_.min_gain_db());
EXPECT_LE(gain_state_.gain_db(), stream_props_.max_gain_db());
received_get_gain_ = true;
});
RunLoopUntil([this]() { return received_get_gain_; });
}
// Determine an appropriate gain state to request, then call other method to request to the
// driver. This method assumes that the driver has already successfully responded to a GetGain
// request.
void BasicTest::RequestSetGain() {
ASSERT_TRUE(received_get_gain_);
if (stream_props_.max_gain_db() == stream_props_.min_gain_db()) {
FX_LOGS(WARNING) << "*** Audio " << ((device_type() == DeviceType::Input) ? "input" : "output")
<< " has fixed gain (" << gain_state_.gain_db()
<< " dB). Skipping SetGain test. ***";
return;
}
EXPECT_EQ(gain_state_.Clone(&set_gain_state_), ZX_OK);
*set_gain_state_.mutable_gain_db() = stream_props_.min_gain_db();
if (gain_state_.gain_db() == stream_props_.min_gain_db()) {
*set_gain_state_.mutable_gain_db() += stream_props_.gain_step_db();
}
fuchsia::hardware::audio::GainState gain_state;
EXPECT_EQ(set_gain_state_.Clone(&gain_state), ZX_OK);
AUDIO_LOG(DEBUG) << "Sent gain " << gain_state.gain_db();
stream_config()->SetGain(std::move(gain_state));
}
// Request that driver retrieve the current plug detection state.
void BasicTest::RequestPlugDetect() {
// 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.
stream_config()->WatchPlugState([this](fuchsia::hardware::audio::PlugState state) {
plug_state_ = std::move(state);
EXPECT_TRUE(plug_state_.has_plugged());
EXPECT_TRUE(plug_state_.has_plug_state_time());
EXPECT_LT(plug_state_.plug_state_time(), zx::clock::get_monotonic().get());
AUDIO_LOG(DEBUG) << "Plug_state_time: " << plug_state_.plug_state_time();
received_plug_detect_ = true;
});
RunLoopUntil([this]() { return received_plug_detect_; });
}
//
// Test cases that target each of the various Stream channel commands
//
// Verify a valid unique_id, manufacturer, product and gain capabilites is successfully received.
TEST_P(BasicTest, StreamProperties) { RequestStreamProperties(); }
// Verify a valid get gain response is successfully received.
TEST_P(BasicTest, GetGain) {
RequestStreamProperties();
RequestGain();
}
// Verify a valid set gain response is successfully received. GetGain is required first: it returns
// not only current gain but also gain capabilities (gain min/max/step, can_mute, can_agc).
TEST_P(BasicTest, SetGain) {
RequestStreamProperties();
RequestGain();
RequestSetGain();
}
// Verify a valid get formats response is successfully received.
TEST_P(BasicTest, GetFormats) {
RequestStreamProperties();
RequestFormats();
}
// Verify a valid plug detect response is successfully received.
TEST_P(BasicTest, PlugDetect) {
RequestStreamProperties();
RequestPlugDetect();
}
// A driver's plug detect updates are not testable without a way to trigger the
// driver's internal hardware-detect mechanism, so that it emits unsolicited PLUG/UNPLUG events.
//
INSTANTIATE_TEST_SUITE_P(AudioDriverTests, BasicTest,
testing::Values(DeviceType::Input, DeviceType::Output),
TestBase::DeviceTypeToString);
} // namespace media::audio::drivers::test