blob: 1e8f3ca55659b18f5cd73bc687aaffce31192275 [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/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 {
// Stream channel requests
//
// Request stream properties including unique ID (which must be unique between input and output).
// TODO(mpuryear): actually 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_ || failed(); });
}
// Request that the driver return its gain capabilities and current state.
void BasicTest::RequestGain() {
ASSERT_FALSE(issued_set_gain_); // Must request gain capabilities before setting gain.
// 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([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());
// We require that audio drivers have a default gain no greater than 0dB.
EXPECT_LE(gain_state_.gain_db(), 0.f);
received_get_gain_ = true;
});
RunLoopUntil([this]() { return received_get_gain_ || failed(); });
}
// 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 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));
issued_set_gain_ = true;
}
// 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_ || failed(); });
}
#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 a valid unique_id, manufacturer, product and gain capabilites is successfully received.
DEFINE_BASIC_TEST_CLASS(StreamProperties, { RequestStreamProperties(); });
// Verify valid get gain and set gain responses are successfully received.
DEFINE_BASIC_TEST_CLASS(GetGain, {
RequestStreamProperties();
RequestGain();
});
DEFINE_BASIC_TEST_CLASS(SetGain, {
RequestStreamProperties();
RequestGain();
RequestSetGain();
});
// Verify valid get formats and plug detect responses are successfully received.
DEFINE_BASIC_TEST_CLASS(GetFormats, {
RequestStreamProperties();
RequestFormats();
});
DEFINE_BASIC_TEST_CLASS(PlugDetect, {
RequestStreamProperties();
RequestPlugDetect();
});
// Someday: 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.
// 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) {
REGISTER_BASIC_TEST(StreamProperties, device_entry);
REGISTER_BASIC_TEST(GetGain, device_entry);
REGISTER_BASIC_TEST(SetGain, device_entry);
REGISTER_BASIC_TEST(GetFormats, device_entry);
REGISTER_BASIC_TEST(PlugDetect, device_entry);
}
} // namespace media::audio::drivers::test