audio: Parametrize core VTS tests

Parametrize tests to accept IDevicesFactory instance name
and IDevice instance name. For audio HAL versions 2..5
the factory instance name is taken from the VTS environment,
and the device is always "primary". For the next versions
the factories are discovered by the test, and the devices
are taken from the audio policy configuration
(to be implemented, see added FIXME comments).

Split the Environment class into two versions: for HAL 2..5
and for next versions. They use different base class.

Move device factories and device caching into dedicated
classes DevicesFactoryManager and DeviceManager. They deal
with instance caching and proper release of server resources.

Bug: 141847510
Bug: 141989952
Test: atest VtsHalAudioV5_0TargetTest
      atest VtsHalAudioV6_0TargetTest
Change-Id: I92c44e0c3f900164dded7e9c4bfc642ca2c335db
Merged-In: I92c44e0c3f900164dded7e9c4bfc642ca2c335db
This commit is contained in:
Mikhail Naganov 2019-10-18 13:09:35 -07:00
parent 315ce41a5d
commit ccd484bb80
7 changed files with 537 additions and 202 deletions

View file

@ -20,9 +20,6 @@
#include <functional>
#include <list>
#include <VtsHalHidlTargetTestEnvBase.h>
#include <gtest/gtest.h>
namespace android {
namespace hardware {
namespace audio {
@ -34,18 +31,20 @@ namespace utility {
* Avoid destroying static objects after main return.
* Post main return destruction leads to incorrect gtest timing measurements as
* well as harder debuging if anything goes wrong during destruction. */
class Environment : public ::testing::VtsHalHidlTargetTestEnvBase {
public:
class EnvironmentTearDown {
public:
using TearDownFunc = std::function<void()>;
void registerTearDown(TearDownFunc&& tearDown) { tearDowns.push_front(std::move(tearDown)); }
private:
void HidlTearDown() override {
protected:
void executeAllTearDowns() {
// Call the tear downs in reverse order of insertion
for (auto& tearDown : tearDowns) {
tearDown();
}
}
private:
std::list<TearDownFunc> tearDowns;
};

View file

@ -60,19 +60,20 @@ TEST_IO_STREAM(SetConnectedState,
"deconnection",
testConnectedState(stream.get()))
TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", ASSERT_IS_OK(device->getHwAvSync()));
TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail",
ASSERT_IS_OK(getDevice()->getHwAvSync()));
TEST_F(AudioPrimaryHidlTest, setMode) {
TEST_P(AudioPrimaryHidlTest, setMode) {
doc::test("Make sure setMode always succeeds if mode is valid and fails otherwise");
// Test Invalid values
for (AudioMode mode : {AudioMode::INVALID, AudioMode::CURRENT, AudioMode::CNT}) {
SCOPED_TRACE("mode=" + toString(mode));
ASSERT_RESULT(Result::INVALID_ARGUMENTS, device->setMode(mode));
ASSERT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(mode));
}
// Test valid values
for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE,
AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) {
SCOPED_TRACE("mode=" + toString(mode));
ASSERT_OK(device->setMode(mode));
ASSERT_OK(getDevice()->setMode(mode));
}
}

View file

@ -0,0 +1,34 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_HARDWARE_AUDIO_CORE_2_0_ENVIRONMENT_TEARDOWN_H
#define ANDROID_HARDWARE_AUDIO_CORE_2_0_ENVIRONMENT_TEARDOWN_H
#include <VtsHalHidlTargetTestEnvBase.h>
#include <gtest/gtest.h>
#include "utility/EnvironmentTearDown.h"
class Environment : public ::android::hardware::audio::common::test::utility::EnvironmentTearDown,
public ::testing::VtsHalHidlTargetTestEnvBase {
private:
void HidlTearDown() override {
executeAllTearDowns();
VtsHalHidlTargetTestEnvBase::HidlTearDown();
}
};
#endif // ANDROID_HARDWARE_AUDIO_CORE_2_0_ENVIRONMENT_TEARDOWN_H

View file

@ -16,25 +16,14 @@
#include "AudioPrimaryHidlHalTest.h"
static void waitForDeviceDestruction() {
// FIXME: there is no way to know when the remote IDevice is being destroyed
// Binder does not support testing if an object is alive, thus
// wait for 100ms to let the binder destruction propagates and
// the remote device has the time to be destroyed.
// flushCommand makes sure all local command are sent, thus should reduce
// the latency between local and remote destruction.
IPCThreadState::self()->flushCommands();
usleep(100 * 1000);
}
TEST_F(AudioHidlTest, OpenPrimaryDeviceUsingGetDevice) {
TEST_P(AudioHidlTest, OpenPrimaryDeviceUsingGetDevice) {
doc::test("Calling openDevice(\"primary\") should return the primary device.");
struct WaitExecutor {
~WaitExecutor() { waitForDeviceDestruction(); }
~WaitExecutor() { DeviceManager::waitForInstanceDestruction(); }
} waitExecutor; // Make sure we wait for the device destruction on exiting from the test.
Result result;
sp<IDevice> baseDevice;
ASSERT_OK(devicesFactory->openDevice("primary", returnIn(result, baseDevice)));
ASSERT_OK(getDevicesFactory()->openDevice("primary", returnIn(result, baseDevice)));
if (result != Result::OK && isPrimaryDeviceOptional()) {
GTEST_SKIP() << "No primary device on this factory"; // returns
}
@ -50,10 +39,10 @@ TEST_F(AudioHidlTest, OpenPrimaryDeviceUsingGetDevice) {
/////////////////////////// get(Active)Microphones ///////////////////////////
//////////////////////////////////////////////////////////////////////////////
TEST_F(AudioPrimaryHidlTest, GetMicrophonesTest) {
TEST_P(AudioPrimaryHidlTest, GetMicrophonesTest) {
doc::test("Make sure getMicrophones always succeeds");
hidl_vec<MicrophoneInfo> microphones;
ASSERT_OK(device->getMicrophones(returnIn(res, microphones)));
ASSERT_OK(getDevice()->getMicrophones(returnIn(res, microphones)));
ASSERT_OK(res);
if (microphones.size() > 0) {
// When there is microphone on the phone, try to open an input stream
@ -75,15 +64,15 @@ TEST_F(AudioPrimaryHidlTest, GetMicrophonesTest) {
}
sp<IStreamIn> stream;
AudioConfig suggestedConfig{};
ASSERT_OK(device->openInputStream(ioHandle, microphone.deviceAddress, config, flags,
initMetadata,
returnIn(res, stream, suggestedConfig)));
ASSERT_OK(getDevice()->openInputStream(ioHandle, microphone.deviceAddress, config,
flags, initMetadata,
returnIn(res, stream, suggestedConfig)));
if (res != Result::OK) {
ASSERT_TRUE(stream == nullptr);
AudioConfig suggestedConfigRetry{};
ASSERT_OK(device->openInputStream(ioHandle, microphone.deviceAddress,
suggestedConfig, flags, initMetadata,
returnIn(res, stream, suggestedConfigRetry)));
ASSERT_OK(getDevice()->openInputStream(
ioHandle, microphone.deviceAddress, suggestedConfig, flags, initMetadata,
returnIn(res, stream, suggestedConfigRetry)));
}
ASSERT_OK(res);
hidl_vec<MicrophoneInfo> activeMicrophones;
@ -131,7 +120,7 @@ TEST_F(AudioPrimaryHidlTest, GetMicrophonesTest) {
}
}
TEST_F(AudioPrimaryHidlTest, SetConnectedState) {
TEST_P(AudioPrimaryHidlTest, SetConnectedState) {
doc::test("Check that the HAL can be notified of device connection and deconnection");
using AD = AudioDevice;
for (auto deviceType : {AD::OUT_HDMI, AD::OUT_WIRED_HEADPHONE, AD::IN_USB_HEADSET}) {
@ -140,7 +129,7 @@ TEST_F(AudioPrimaryHidlTest, SetConnectedState) {
SCOPED_TRACE("state=" + ::testing::PrintToString(state));
DeviceAddress address = {};
address.device = deviceType;
auto ret = device->setConnectedState(address, state);
auto ret = getDevice()->setConnectedState(address, state);
ASSERT_TRUE(ret.isOk());
if (ret == Result::NOT_SUPPORTED) {
doc::partialTest("setConnectedState is not supported");
@ -153,9 +142,7 @@ TEST_F(AudioPrimaryHidlTest, SetConnectedState) {
// Because there is no way of knowing if the devices were connected before
// calling setConnectedState, there is no way to restore the HAL to its
// initial state. To workaround this, destroy the HAL at the end of this test.
device.clear();
waitForDeviceDestruction();
ASSERT_NO_FATAL_FAILURE(initPrimaryDevice());
ASSERT_TRUE(DeviceManager::getInstance().resetPrimary(getFactoryName()));
}
static void testGetDevices(IStream* stream, AudioDevice expectedDevice) {
@ -199,7 +186,7 @@ static void checkGetHwAVSync(IDevice* device) {
}
ASSERT_OK(res);
}
TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", checkGetHwAVSync(device.get()));
TEST_IO_STREAM(GetHwAvSync, "Get hardware sync can not fail", checkGetHwAVSync(getDevice().get()));
TEST_P(InputStreamTest, updateSinkMetadata) {
doc::test("The HAL should not crash on metadata change");
@ -259,58 +246,58 @@ TEST_P(OutputStreamTest, updateSourceMetadata) {
ASSERT_OK(stream->updateSourceMetadata(initMetadata));
}
TEST_F(AudioPrimaryHidlTest, setMode) {
TEST_P(AudioPrimaryHidlTest, setMode) {
doc::test("Make sure setMode always succeeds if mode is valid and fails otherwise");
// Test Invalid values
for (int mode : {-2, -1, int(AudioMode::IN_COMMUNICATION) + 1}) {
ASSERT_RESULT(Result::INVALID_ARGUMENTS, device->setMode(AudioMode(mode)))
<< "mode=" << mode;
ASSERT_RESULT(Result::INVALID_ARGUMENTS, getDevice()->setMode(AudioMode(mode)))
<< "mode=" << mode;
}
// Test valid values
for (AudioMode mode : {AudioMode::IN_CALL, AudioMode::IN_COMMUNICATION, AudioMode::RINGTONE,
AudioMode::NORMAL /* Make sure to leave the test in normal mode */}) {
ASSERT_OK(device->setMode(mode)) << "mode=" << toString(mode);
ASSERT_OK(getDevice()->setMode(mode)) << "mode=" << toString(mode);
}
}
TEST_F(AudioPrimaryHidlTest, setBtHfpSampleRate) {
TEST_P(AudioPrimaryHidlTest, setBtHfpSampleRate) {
doc::test(
"Make sure setBtHfpSampleRate either succeeds or "
"indicates that it is not supported at all, or that the provided value is invalid");
for (auto samplingRate : {8000, 16000, 22050, 24000}) {
ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, device->setBtHfpSampleRate(samplingRate));
ASSERT_RESULT(okOrNotSupportedOrInvalidArgs, getDevice()->setBtHfpSampleRate(samplingRate));
}
}
TEST_F(AudioPrimaryHidlTest, setBtHfpVolume) {
TEST_P(AudioPrimaryHidlTest, setBtHfpVolume) {
doc::test(
"Make sure setBtHfpVolume is either not supported or "
"only succeed if volume is in [0,1]");
auto ret = device->setBtHfpVolume(0.0);
auto ret = getDevice()->setBtHfpVolume(0.0);
ASSERT_TRUE(ret.isOk());
if (ret == Result::NOT_SUPPORTED) {
doc::partialTest("setBtHfpVolume is not supported");
return;
}
testUnitaryGain([](float volume) { return device->setBtHfpVolume(volume); });
testUnitaryGain([this](float volume) { return getDevice()->setBtHfpVolume(volume); });
}
TEST_F(AudioPrimaryHidlTest, setBtScoHeadsetDebugName) {
TEST_P(AudioPrimaryHidlTest, setBtScoHeadsetDebugName) {
doc::test(
"Make sure setBtScoHeadsetDebugName either succeeds or "
"indicates that it is not supported");
ASSERT_RESULT(okOrNotSupported, device->setBtScoHeadsetDebugName("test"));
ASSERT_RESULT(okOrNotSupported, getDevice()->setBtScoHeadsetDebugName("test"));
}
TEST_F(AudioPrimaryHidlTest, updateRotation) {
TEST_P(AudioPrimaryHidlTest, updateRotation) {
doc::test("Check that the hal can receive the current rotation");
for (Rotation rotation : {Rotation::DEG_0, Rotation::DEG_90, Rotation::DEG_180,
Rotation::DEG_270, Rotation::DEG_0}) {
ASSERT_RESULT(okOrNotSupported, device->updateRotation(rotation));
ASSERT_RESULT(okOrNotSupported, getDevice()->updateRotation(rotation));
}
}
TEST_F(BoolAccessorPrimaryHidlTest, setGetBtHfpEnabled) {
TEST_P(BoolAccessorPrimaryHidlTest, setGetBtHfpEnabled) {
doc::test("Query and set the BT HFP state");
testAccessors<OPTIONAL>("BtHfpEnabled", Initial{false, OPTIONAL}, {true},
&IPrimaryDevice::setBtHfpEnabled, &IPrimaryDevice::getBtHfpEnabled);

View file

@ -0,0 +1,35 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ANDROID_HARDWARE_AUDIO_CORE_6_0_ENVIRONMENT_TEARDOWN_H
#define ANDROID_HARDWARE_AUDIO_CORE_6_0_ENVIRONMENT_TEARDOWN_H
#include <gtest/gtest.h>
#include "utility/EnvironmentTearDown.h"
class Environment : public ::android::hardware::audio::common::test::utility::EnvironmentTearDown,
public ::testing::Environment {
public:
void init(int* /*argc*/, char** /*argv*/) {} // emulate VtsHalHidlTargetTestEnvBase
private:
void TearDown() override { executeAllTearDowns(); }
};
// FIXME: Will be removed while making getDeviceParameters to use the config
static constexpr const char* kDefaultServiceName = "default";
#endif // ANDROID_HARDWARE_AUDIO_CORE_6_0_ENVIRONMENT_TEARDOWN_H

View file

@ -23,6 +23,7 @@
#include <initializer_list>
#include <limits>
#include <list>
#include <map>
#include <string>
#include <vector>
@ -31,7 +32,9 @@
#include <hwbinder/IPCThreadState.h>
#if MAJOR_VERSION <= 5
#include <VtsHalHidlTargetTestBase.h>
#endif
#include <android-base/logging.h>
@ -44,16 +47,25 @@
#include <Serializer.h>
#include <fmq/EventFlag.h>
#include <fmq/MessageQueue.h>
#if MAJOR_VERSION >= 6
#include <hidl/GtestPrinter.h>
#include <hidl/ServiceManagement.h>
#endif
#include <common/all-versions/VersionUtils.h>
#include "utility/AssertOk.h"
#include "utility/Documentation.h"
#include "utility/EnvironmentTearDown.h"
#include "utility/PrettyPrintAudioTypes.h"
#include "utility/ReturnIn.h"
#include "utility/ValidateXml.h"
#if MAJOR_VERSION <= 5
#include "2.0/EnvironmentTearDown.h"
#elif MAJOR_VERSION >= 6
#include "6.0/EnvironmentTearDown.h"
#endif
/** Provide version specific functions that are used in the generic tests */
#if MAJOR_VERSION == 2
#include "2.0/AudioPrimaryHidlHalUtils.h"
@ -105,14 +117,23 @@ static auto invalidStateOrNotSupported = {Result::INVALID_STATE, Result::NOT_SUP
class AudioHidlTestEnvironment : public ::Environment {
public:
virtual void registerTestServices() override { registerTestService<IDevicesFactory>(); }
#if MAJOR_VERSION <= 5
void registerTestServices() override { registerTestService<IDevicesFactory>(); }
#endif
};
// Instance to register global tearDown
static AudioHidlTestEnvironment* environment;
#define AUDIO_PRIMARY_HIDL_HAL_TEST
#include "DeviceManager.h"
#if MAJOR_VERSION <= 5
class HidlTest : public ::testing::VtsHalHidlTargetTestBase {
protected:
#elif MAJOR_VERSION >= 6
class HidlTest : public ::testing::Test {
#endif
protected:
// Convenient member to store results
Result res;
};
@ -201,6 +222,21 @@ class AudioPolicyConfigTest : public HidlTest {
ASSERT_TRUE(mPrimaryConfig) << "Could not find primary module in configuration file: "
<< policyConfig.getFilePath();
}
protected:
sp<IDevicesFactory> getDevicesFactory(const std::string& factoryName) const {
return DevicesFactoryManager::getInstance().get(factoryName);
}
sp<IPrimaryDevice> getPrimaryDevice(const std::string& factoryName) const {
return DeviceManager::getInstance().getPrimary(factoryName);
}
bool isPrimaryDeviceOptional(const std::string& factoryName) const {
// It's OK not to have "primary" device on non-default audio HAL service.
return factoryName != kDefaultServiceName;
}
sp<const HwModule> mPrimaryConfig = nullptr;
};
@ -208,42 +244,86 @@ TEST_F(AudioPolicyConfigTest, LoadAudioPolicyXMLConfiguration) {
doc::test("Test parsing audio_policy_configuration.xml (called in SetUp)");
}
//////////////////////////////////////////////////////////////////////////////
//////////////////// Test parameter types and definitions ////////////////////
//////////////////////////////////////////////////////////////////////////////
enum { PARAM_FACTORY_NAME, PARAM_DEVICE_NAME };
using DeviceParameter = std::tuple<std::string, std::string>;
static inline std::string DeviceParameterToString(
const ::testing::TestParamInfo<DeviceParameter>& info) {
#if MAJOR_VERSION <= 5
return std::get<PARAM_DEVICE_NAME>(info.param);
#elif MAJOR_VERSION >= 6
const auto factoryName =
::android::hardware::PrintInstanceNameToString(::testing::TestParamInfo<std::string>{
std::get<PARAM_FACTORY_NAME>(info.param), info.index});
const auto& deviceName = std::get<PARAM_DEVICE_NAME>(info.param);
return !deviceName.empty() ? factoryName + "_" + deviceName : factoryName;
#endif
}
#if MAJOR_VERSION <= 5
// For V2..5 the factory is looked up using the instance name passed
// in the environment, only one factory is returned. This is because the VTS
// framework will call the test for each instance. Only the primary device of
// the factory specified in the environment is tested.
const std::vector<DeviceParameter>& getDeviceParameters() {
static std::vector<DeviceParameter> parameters = {
{environment->getServiceName<IDevicesFactory>(), DeviceManager::kPrimaryDevice}};
return parameters;
}
#elif MAJOR_VERSION >= 6
// FIXME: Will be replaced with code that analyzes the audio policy config file.
const std::vector<DeviceParameter>& getDeviceParameters() {
static std::vector<DeviceParameter> parameters = [] {
const auto instances =
::android::hardware::getAllHalInstanceNames(IDevicesFactory::descriptor);
std::vector<DeviceParameter> result;
result.reserve(instances.size());
for (const auto& instance : instances) {
result.emplace_back(instance, DeviceManager::kPrimaryDevice);
}
return result;
}();
return parameters;
}
#endif
class AudioHidlTestWithDeviceParameter : public AudioPolicyConfigTest,
public ::testing::WithParamInterface<DeviceParameter> {
protected:
const std::string& getFactoryName() const { return std::get<PARAM_FACTORY_NAME>(GetParam()); }
bool isPrimaryDeviceOptional() const {
return AudioPolicyConfigTest::isPrimaryDeviceOptional(getFactoryName());
}
sp<IDevicesFactory> getDevicesFactory() const {
return AudioPolicyConfigTest::getDevicesFactory(getFactoryName());
}
sp<IPrimaryDevice> getPrimaryDevice() const {
return AudioPolicyConfigTest::getPrimaryDevice(getFactoryName());
}
};
//////////////////////////////////////////////////////////////////////////////
////////////////////// getService audio_devices_factory //////////////////////
//////////////////////////////////////////////////////////////////////////////
// Test all audio devices
class AudioHidlTest : public AudioPolicyConfigTest {
public:
static void SetUpTestSuite() {
devicesFactory = ::testing::VtsHalHidlTargetTestBase::getService<IDevicesFactory>(
environment->getServiceName<IDevicesFactory>());
}
static void TearDownTestSuite() { devicesFactory.clear(); }
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(AudioPolicyConfigTest::SetUp()); // setup base
// Failures during SetUpTestSuite do not cause test termination.
ASSERT_TRUE(devicesFactory != nullptr);
}
protected:
// Cache the devicesFactory retrieval to speed up each test by ~0.5s
static sp<IDevicesFactory> devicesFactory;
static bool isPrimaryDeviceOptional() {
// It's OK not to have "primary" device on non-default audio HAL service.
return environment->getServiceName<IDevicesFactory>() != kDefaultServiceName;
class AudioHidlTest : public AudioHidlTestWithDeviceParameter {
public:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(AudioHidlTestWithDeviceParameter::SetUp()); // setup base
ASSERT_TRUE(getDevicesFactory() != nullptr);
}
};
sp<IDevicesFactory> AudioHidlTest::devicesFactory;
TEST_F(AudioHidlTest, GetAudioDevicesFactoryService) {
TEST_P(AudioHidlTest, GetAudioDevicesFactoryService) {
doc::test("Test the getService");
}
TEST_F(AudioHidlTest, OpenDeviceInvalidParameter) {
TEST_P(AudioHidlTest, OpenDeviceInvalidParameter) {
doc::test("Test passing an invalid parameter to openDevice");
Result result;
sp<IDevice> device;
@ -252,11 +332,14 @@ TEST_F(AudioHidlTest, OpenDeviceInvalidParameter) {
#elif MAJOR_VERSION >= 4
auto invalidDevice = "Non existing device";
#endif
ASSERT_OK(devicesFactory->openDevice(invalidDevice, returnIn(result, device)));
ASSERT_OK(getDevicesFactory()->openDevice(invalidDevice, returnIn(result, device)));
ASSERT_EQ(Result::INVALID_ARGUMENTS, result);
ASSERT_TRUE(device == nullptr);
}
INSTANTIATE_TEST_CASE_P(AudioHidl, AudioHidlTest, ::testing::ValuesIn(getDeviceParameters()),
&DeviceParameterToString);
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////// openDevice primary ///////////////////////////
//////////////////////////////////////////////////////////////////////////////
@ -264,57 +347,30 @@ TEST_F(AudioHidlTest, OpenDeviceInvalidParameter) {
// Test the primary device
class AudioPrimaryHidlTest : public AudioHidlTest {
public:
static void SetUpTestSuite() {
ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUpTestSuite());
ASSERT_NO_FATAL_FAILURE(initPrimaryDevice());
}
static void TearDownTestSuite() {
device.clear();
AudioHidlTest::TearDownTestSuite();
}
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base
if (device == nullptr && isPrimaryDeviceOptional()) {
if (getDevice() == nullptr && isPrimaryDeviceOptional()) {
GTEST_SKIP() << "No primary device on this factory";
}
ASSERT_TRUE(device != nullptr);
ASSERT_TRUE(getDevice() != nullptr);
}
protected:
// Cache the device opening to speed up each test by ~0.5s
static sp<IPrimaryDevice> device;
static void initPrimaryDevice() {
// Failures during test suite set up do not cause test termination.
ASSERT_TRUE(devicesFactory != nullptr);
Result result;
#if MAJOR_VERSION == 2
sp<IDevice> baseDevice;
ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY,
returnIn(result, baseDevice)));
ASSERT_OK(result);
ASSERT_TRUE(baseDevice != nullptr);
device = IPrimaryDevice::castFrom(baseDevice);
#elif MAJOR_VERSION >= 4
ASSERT_OK(devicesFactory->openPrimaryDevice(returnIn(result, device)));
ASSERT_OK(result);
#endif
}
sp<IPrimaryDevice> getDevice() const { return getPrimaryDevice(); }
};
sp<IPrimaryDevice> AudioPrimaryHidlTest::device;
TEST_F(AudioPrimaryHidlTest, OpenPrimaryDevice) {
TEST_P(AudioPrimaryHidlTest, OpenPrimaryDevice) {
doc::test("Test the openDevice (called during setup)");
}
TEST_F(AudioPrimaryHidlTest, Init) {
TEST_P(AudioPrimaryHidlTest, Init) {
doc::test("Test that the audio primary hal initialized correctly");
ASSERT_OK(device->initCheck());
ASSERT_OK(getDevice()->initCheck());
}
INSTANTIATE_TEST_CASE_P(AudioPrimaryHidl, AudioPrimaryHidlTest,
::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
//////////////////////////////////////////////////////////////////////////////
///////////////////// {set,get}{Master,Mic}{Mute,Volume} /////////////////////
//////////////////////////////////////////////////////////////////////////////
@ -339,7 +395,7 @@ class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
optionality == OPTIONAL ? Result::NOT_SUPPORTED : Result::OK};
Property initialValue = expectedInitial.value;
ASSERT_OK((device.get()->*getter)(returnIn(res, initialValue)));
ASSERT_OK((getDevice().get()->*getter)(returnIn(res, initialValue)));
ASSERT_RESULT(expectedResults, res);
if (res == Result::OK && expectedInitial.check == REQUIRED) {
EXPECT_EQ(expectedInitial.value, initialValue);
@ -350,7 +406,7 @@ class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
for (Property setValue : valuesToTest) {
SCOPED_TRACE("Test " + propertyName + " getter and setter for " +
testing::PrintToString(setValue));
auto ret = (device.get()->*setter)(setValue);
auto ret = (getDevice().get()->*setter)(setValue);
ASSERT_RESULT(expectedResults, ret);
if (ret == Result::NOT_SUPPORTED) {
doc::partialTest(propertyName + " setter is not supported");
@ -358,7 +414,7 @@ class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
}
Property getValue;
// Make sure the getter returns the same value just set
ASSERT_OK((device.get()->*getter)(returnIn(res, getValue)));
ASSERT_OK((getDevice().get()->*getter)(returnIn(res, getValue)));
ASSERT_RESULT(expectedResults, res);
if (res == Result::NOT_SUPPORTED) {
doc::partialTest(propertyName + " getter is not supported");
@ -370,31 +426,34 @@ class AccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
for (Property invalidValue : invalidValues) {
SCOPED_TRACE("Try to set " + propertyName + " with the invalid value " +
testing::PrintToString(invalidValue));
EXPECT_RESULT(invalidArgsOrNotSupported, (device.get()->*setter)(invalidValue));
EXPECT_RESULT(invalidArgsOrNotSupported, (getDevice().get()->*setter)(invalidValue));
}
// Restore initial value
EXPECT_RESULT(expectedResults, (device.get()->*setter)(initialValue));
EXPECT_RESULT(expectedResults, (getDevice().get()->*setter)(initialValue));
}
};
using BoolAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<bool>;
TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) {
TEST_P(BoolAccessorPrimaryHidlTest, MicMuteTest) {
doc::test("Check that the mic can be muted and unmuted");
testAccessors("mic mute", Initial{false}, {true}, &IDevice::setMicMute, &IDevice::getMicMute);
// TODO: check that the mic is really muted (all sample are 0)
}
TEST_F(BoolAccessorPrimaryHidlTest, MasterMuteTest) {
TEST_P(BoolAccessorPrimaryHidlTest, MasterMuteTest) {
doc::test("If master mute is supported, try to mute and unmute the master output");
testAccessors<OPTIONAL>("master mute", Initial{false}, {true}, &IDevice::setMasterMute,
&IDevice::getMasterMute);
// TODO: check that the master volume is really muted
}
INSTANTIATE_TEST_CASE_P(BoolAccessorPrimaryHidl, BoolAccessorPrimaryHidlTest,
::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
using FloatAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<float>;
TEST_F(FloatAccessorPrimaryHidlTest, MasterVolumeTest) {
TEST_P(FloatAccessorPrimaryHidlTest, MasterVolumeTest) {
doc::test("Test the master volume if supported");
testAccessors<OPTIONAL>(
"master volume", Initial{1}, {0, 0.5}, &IDevice::setMasterVolume, &IDevice::getMasterVolume,
@ -402,16 +461,19 @@ TEST_F(FloatAccessorPrimaryHidlTest, MasterVolumeTest) {
// TODO: check that the master volume is really changed
}
INSTANTIATE_TEST_CASE_P(FloatAccessorPrimaryHidl, FloatAccessorPrimaryHidlTest,
::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
//////////////////////////////////////////////////////////////////////////////
//////////////////////////////// AudioPatches ////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
class AudioPatchPrimaryHidlTest : public AudioPrimaryHidlTest {
protected:
bool areAudioPatchesSupported() { return extract(device->supportsAudioPatches()); }
bool areAudioPatchesSupported() { return extract(getDevice()->supportsAudioPatches()); }
};
TEST_F(AudioPatchPrimaryHidlTest, AudioPatches) {
TEST_P(AudioPatchPrimaryHidlTest, AudioPatches) {
doc::test("Test if audio patches are supported");
if (!areAudioPatchesSupported()) {
doc::partialTest("Audio patches are not supported");
@ -420,6 +482,9 @@ TEST_F(AudioPatchPrimaryHidlTest, AudioPatches) {
// TODO: test audio patches
}
INSTANTIATE_TEST_CASE_P(AudioPatchPrimaryHidl, AudioPatchPrimaryHidlTest,
::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
//////////////////////////////////////////////////////////////////////////////
//////////////// Required and recommended audio format support ///////////////
// From:
@ -429,8 +494,7 @@ TEST_F(AudioPatchPrimaryHidlTest, AudioPatches) {
/////////// TODO: move to the beginning of the file for easier update ////////
//////////////////////////////////////////////////////////////////////////////
class AudioConfigPrimaryTest : public AudioPatchPrimaryHidlTest {
public:
struct ConfigHelper {
// for retro compatibility only test the primary device IN_BUILTIN_MIC
// FIXME: in the next audio HAL version, test all available devices
static bool primaryHasMic() {
@ -502,21 +566,51 @@ class AudioConfigPrimaryTest : public AudioPatchPrimaryHidlTest {
}
};
// Nesting a tuple in another tuple allows to use GTest Combine function to generate
// all combinations of devices and configs.
enum { PARAM_DEVICE, PARAM_CONFIG };
using DeviceConfigParameter = std::tuple<DeviceParameter, AudioConfig>;
/** Generate a test name based on an audio config.
*
* As the only parameter changing are channel mask and sample rate,
* only print those ones in the test name.
*/
static string generateTestName(const testing::TestParamInfo<AudioConfig>& info) {
const AudioConfig& config = info.param;
return to_string(info.index) + "__" + to_string(config.sampleRateHz) + "_" +
static string DeviceConfigParameterToString(
const testing::TestParamInfo<DeviceConfigParameter>& info) {
const AudioConfig& config = std::get<PARAM_CONFIG>(info.param);
const auto deviceName = DeviceParameterToString(::testing::TestParamInfo<DeviceParameter>{
std::get<PARAM_DEVICE>(info.param), info.index});
return (deviceName.empty() ? "" : deviceName + "_") + to_string(info.index) + "__" +
to_string(config.sampleRateHz) + "_" +
// "MONO" is more clear than "FRONT_LEFT"
((config.channelMask == mkEnumBitfield(AudioChannelMask::OUT_MONO) ||
config.channelMask == mkEnumBitfield(AudioChannelMask::IN_MONO))
? "MONO"
: ::testing::PrintToString(config.channelMask));
? "MONO"
: ::testing::PrintToString(config.channelMask));
}
class AudioHidlTestWithDeviceConfigParameter
: public AudioPolicyConfigTest,
public ::testing::WithParamInterface<DeviceConfigParameter> {
protected:
const std::string& getFactoryName() const {
return std::get<PARAM_FACTORY_NAME>(std::get<PARAM_DEVICE>(GetParam()));
}
bool isPrimaryDeviceOptional() const {
return AudioPolicyConfigTest::isPrimaryDeviceOptional(getFactoryName());
}
sp<IDevicesFactory> getDevicesFactory() const {
return AudioPolicyConfigTest::getDevicesFactory(getFactoryName());
}
sp<IPrimaryDevice> getPrimaryDevice() const {
return AudioPolicyConfigTest::getPrimaryDevice(getFactoryName());
}
const AudioConfig& getConfig() const { return std::get<PARAM_CONFIG>(GetParam()); }
// FIXME: Split out tests that don't require primary device
sp<IPrimaryDevice> getDevice() const { return getPrimaryDevice(); }
};
//////////////////////////////////////////////////////////////////////////////
///////////////////////////// getInputBufferSize /////////////////////////////
//////////////////////////////////////////////////////////////////////////////
@ -525,12 +619,21 @@ static string generateTestName(const testing::TestParamInfo<AudioConfig>& info)
// android.hardware.microphone
// how to get this value ? is it a property ???
class AudioCaptureConfigPrimaryTest : public AudioConfigPrimaryTest,
public ::testing::WithParamInterface<AudioConfig> {
protected:
class AudioCaptureConfigPrimaryTest : public AudioHidlTestWithDeviceConfigParameter {
public:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(AudioHidlTestWithDeviceConfigParameter::SetUp()); // setup base
ASSERT_TRUE(getDevicesFactory() != nullptr);
if (getDevice() == nullptr && isPrimaryDeviceOptional()) {
GTEST_SKIP() << "No primary device on this factory";
}
ASSERT_TRUE(getDevice() != nullptr);
}
protected:
void inputBufferSizeTest(const AudioConfig& audioConfig, bool supportRequired) {
uint64_t bufferSize;
ASSERT_OK(device->getInputBufferSize(audioConfig, returnIn(res, bufferSize)));
ASSERT_OK(getDevice()->getInputBufferSize(audioConfig, returnIn(res, bufferSize)));
switch (res) {
case Result::INVALID_ARGUMENTS:
@ -554,16 +657,19 @@ TEST_P(RequiredInputBufferSizeTest, RequiredInputBufferSizeTest) {
doc::test(
"Input buffer size must be retrievable for a format with required "
"support.");
inputBufferSizeTest(GetParam(), true);
inputBufferSizeTest(getConfig(), true);
}
INSTANTIATE_TEST_CASE_P(
RequiredInputBufferSize, RequiredInputBufferSizeTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()),
&generateTestName);
RequiredInputBufferSize, RequiredInputBufferSizeTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getRequiredSupportCaptureAudioConfig())),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
SupportedInputBufferSize, RequiredInputBufferSizeTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()),
&generateTestName);
SupportedInputBufferSize, RequiredInputBufferSizeTest,
::testing::Combine(::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getSupportedCaptureAudioConfig())),
&DeviceConfigParameterToString);
// Test that the recommended capture config are supported or lead to a
// INVALID_ARGUMENTS return
@ -572,21 +678,23 @@ TEST_P(OptionalInputBufferSizeTest, OptionalInputBufferSizeTest) {
doc::test(
"Input buffer size should be retrievable for a format with recommended "
"support.");
inputBufferSizeTest(GetParam(), false);
inputBufferSizeTest(getConfig(), false);
}
INSTANTIATE_TEST_CASE_P(
RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()),
&generateTestName);
RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getRecommendedSupportCaptureAudioConfig())),
&DeviceConfigParameterToString);
//////////////////////////////////////////////////////////////////////////////
/////////////////////////////// setScreenState ///////////////////////////////
//////////////////////////////////////////////////////////////////////////////
TEST_F(AudioPrimaryHidlTest, setScreenState) {
TEST_P(AudioPrimaryHidlTest, setScreenState) {
doc::test("Check that the hal can receive the screen state");
for (bool turnedOn : {false, true, true, false, false}) {
ASSERT_RESULT(okOrNotSupported, device->setScreenState(turnedOn));
ASSERT_RESULT(okOrNotSupported, getDevice()->setScreenState(turnedOn));
}
}
@ -594,15 +702,15 @@ TEST_F(AudioPrimaryHidlTest, setScreenState) {
//////////////////////////// {get,set}Parameters /////////////////////////////
//////////////////////////////////////////////////////////////////////////////
TEST_F(AudioPrimaryHidlTest, getParameters) {
TEST_P(AudioPrimaryHidlTest, getParameters) {
doc::test("Check that the hal can set and get parameters");
hidl_vec<ParameterValue> context;
hidl_vec<hidl_string> keys;
hidl_vec<ParameterValue> values;
ASSERT_OK(Parameters::get(device, keys, returnIn(res, values)));
ASSERT_OK(Parameters::set(device, values));
ASSERT_OK(Parameters::get(getDevice(), keys, returnIn(res, values)));
ASSERT_OK(Parameters::set(getDevice(), values));
values.resize(0);
ASSERT_OK(Parameters::set(device, values));
ASSERT_OK(Parameters::set(getDevice(), values));
}
//////////////////////////////////////////////////////////////////////////////
@ -643,14 +751,14 @@ static void testDebugDump(DebugDump debugDump) {
EXPECT_EQ(0, close(fds[1])) << errno;
}
TEST_F(AudioPrimaryHidlTest, DebugDump) {
TEST_P(AudioPrimaryHidlTest, DebugDump) {
doc::test("Check that the hal can dump its state without error");
testDebugDump([](const auto& handle) { return dump(device, handle); });
testDebugDump([this](const auto& handle) { return dump(getDevice(), handle); });
}
TEST_F(AudioPrimaryHidlTest, DebugDumpInvalidArguments) {
TEST_P(AudioPrimaryHidlTest, DebugDumpInvalidArguments) {
doc::test("Check that the hal dump doesn't crash on invalid arguments");
ASSERT_OK(dump(device, hidl_handle()));
ASSERT_OK(dump(getDevice(), hidl_handle()));
}
//////////////////////////////////////////////////////////////////////////////
@ -658,9 +766,17 @@ TEST_F(AudioPrimaryHidlTest, DebugDumpInvalidArguments) {
//////////////////////////////////////////////////////////////////////////////
template <class Stream>
class OpenStreamTest : public AudioConfigPrimaryTest,
public ::testing::WithParamInterface<AudioConfig> {
protected:
class OpenStreamTest : public AudioHidlTestWithDeviceConfigParameter {
protected:
void SetUp() override {
ASSERT_NO_FATAL_FAILURE(AudioHidlTestWithDeviceConfigParameter::SetUp()); // setup base
ASSERT_TRUE(getDevicesFactory() != nullptr);
if (getDevice() == nullptr && isPrimaryDeviceOptional()) {
GTEST_SKIP() << "No primary device on this factory";
}
ASSERT_TRUE(getDevice() != nullptr);
}
template <class Open>
void testOpen(Open openStream, const AudioConfig& config) {
// FIXME: Open a stream without an IOHandle
@ -701,7 +817,7 @@ class OpenStreamTest : public AudioConfigPrimaryTest,
return res;
}
void waitForStreamDestruction() {
static void waitForStreamDestruction() {
// FIXME: there is no way to know when the remote IStream is being destroyed
// Binder does not support testing if an object is alive, thus
// wait for 100ms to let the binder destruction propagates and
@ -712,12 +828,14 @@ class OpenStreamTest : public AudioConfigPrimaryTest,
usleep(100 * 1000);
}
private:
bool areAudioPatchesSupported() { return extract(getDevice()->supportsAudioPatches()); }
private:
void TearDown() override {
if (open) {
ASSERT_OK(closeStream());
}
AudioConfigPrimaryTest::TearDown();
AudioPolicyConfigTest::TearDown();
}
protected:
@ -734,18 +852,19 @@ class OutputStreamTest : public OpenStreamTest<IStreamOut> {
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
if (IsSkipped()) return; // do not attempt to use 'device'
address.device = AudioDevice::OUT_DEFAULT;
const AudioConfig& config = GetParam();
const AudioConfig& config = getConfig();
// TODO: test all flag combination
auto flags = mkEnumBitfield(AudioOutputFlag::NONE);
testOpen(
[&](AudioIoHandle handle, AudioConfig config, auto cb) {
[&](AudioIoHandle handle, AudioConfig config, auto cb) {
#if MAJOR_VERSION == 2
return device->openOutputStream(handle, address, config, flags, cb);
return getDevice()->openOutputStream(handle, address, config, flags, cb);
#elif MAJOR_VERSION >= 4
return device->openOutputStream(handle, address, config, flags, initMetadata, cb);
return getDevice()->openOutputStream(handle, address, config, flags,
initMetadata, cb);
#endif
},
config);
},
config);
}
#if MAJOR_VERSION >= 4
@ -763,18 +882,23 @@ TEST_P(OutputStreamTest, OpenOutputStreamTest) {
// Open done in SetUp
}
INSTANTIATE_TEST_CASE_P(
RequiredOutputStreamConfigSupport, OutputStreamTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportPlaybackAudioConfig()),
&generateTestName);
RequiredOutputStreamConfigSupport, OutputStreamTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getRequiredSupportPlaybackAudioConfig())),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
SupportedOutputStreamConfig, OutputStreamTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedPlaybackAudioConfig()),
&generateTestName);
SupportedOutputStreamConfig, OutputStreamTest,
::testing::Combine(::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getSupportedPlaybackAudioConfig())),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
RecommendedOutputStreamConfigSupport, OutputStreamTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportPlaybackAudioConfig()),
&generateTestName);
RecommendedOutputStreamConfigSupport, OutputStreamTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getRecommendedSupportPlaybackAudioConfig())),
&DeviceConfigParameterToString);
////////////////////////////// openInputStream //////////////////////////////
@ -783,14 +907,15 @@ class InputStreamTest : public OpenStreamTest<IStreamIn> {
ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
if (IsSkipped()) return; // do not attempt to use 'device'
address.device = AudioDevice::IN_DEFAULT;
const AudioConfig& config = GetParam();
const AudioConfig& config = getConfig();
// TODO: test all supported flags and source
auto flags = mkEnumBitfield(AudioInputFlag::NONE);
testOpen(
[&](AudioIoHandle handle, AudioConfig config, auto cb) {
return device->openInputStream(handle, address, config, flags, initMetadata, cb);
},
config);
[&](AudioIoHandle handle, AudioConfig config, auto cb) {
return getDevice()->openInputStream(handle, address, config, flags,
initMetadata, cb);
},
config);
}
protected:
@ -808,18 +933,23 @@ TEST_P(InputStreamTest, OpenInputStreamTest) {
// Open done in setup
}
INSTANTIATE_TEST_CASE_P(
RequiredInputStreamConfigSupport, InputStreamTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()),
&generateTestName);
RequiredInputStreamConfigSupport, InputStreamTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getRequiredSupportCaptureAudioConfig())),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
SupportedInputStreamConfig, InputStreamTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()),
&generateTestName);
SupportedInputStreamConfig, InputStreamTest,
::testing::Combine(::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getSupportedCaptureAudioConfig())),
&DeviceConfigParameterToString);
INSTANTIATE_TEST_CASE_P(
RecommendedInputStreamConfigSupport, InputStreamTest,
::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()),
&generateTestName);
RecommendedInputStreamConfigSupport, InputStreamTest,
::testing::Combine(
::testing::ValuesIn(getDeviceParameters()),
::testing::ValuesIn(ConfigHelper::getRecommendedSupportCaptureAudioConfig())),
&DeviceConfigParameterToString);
//////////////////////////////////////////////////////////////////////////////
////////////////////////////// IStream getters ///////////////////////////////
@ -1332,19 +1462,19 @@ TEST_P(OutputStreamTest, GetPresentationPositionStop) {
/////////////////////////////// PrimaryDevice ////////////////////////////////
//////////////////////////////////////////////////////////////////////////////
TEST_F(AudioPrimaryHidlTest, setVoiceVolume) {
TEST_P(AudioPrimaryHidlTest, setVoiceVolume) {
doc::test("Make sure setVoiceVolume only succeed if volume is in [0,1]");
testUnitaryGain([](float volume) { return device->setVoiceVolume(volume); });
testUnitaryGain([this](float volume) { return getDevice()->setVoiceVolume(volume); });
}
TEST_F(BoolAccessorPrimaryHidlTest, BtScoNrecEnabled) {
TEST_P(BoolAccessorPrimaryHidlTest, BtScoNrecEnabled) {
doc::test("Query and set the BT SCO NR&EC state");
testAccessors<OPTIONAL>("BtScoNrecEnabled", Initial{false, OPTIONAL}, {true},
&IPrimaryDevice::setBtScoNrecEnabled,
&IPrimaryDevice::getBtScoNrecEnabled);
}
TEST_F(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) {
TEST_P(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) {
doc::test("Query and set the SCO whideband state");
testAccessors<OPTIONAL>("BtScoWideband", Initial{false, OPTIONAL}, {true},
&IPrimaryDevice::setBtScoWidebandEnabled,
@ -1352,15 +1482,17 @@ TEST_F(BoolAccessorPrimaryHidlTest, setGetBtScoWidebandEnabled) {
}
using TtyModeAccessorPrimaryHidlTest = AccessorPrimaryHidlTest<IPrimaryDevice::TtyMode>;
TEST_F(TtyModeAccessorPrimaryHidlTest, setGetTtyMode) {
TEST_P(TtyModeAccessorPrimaryHidlTest, setGetTtyMode) {
doc::test("Query and set the TTY mode state");
testAccessors<OPTIONAL>(
"TTY mode", Initial{IPrimaryDevice::TtyMode::OFF},
{IPrimaryDevice::TtyMode::HCO, IPrimaryDevice::TtyMode::VCO, IPrimaryDevice::TtyMode::FULL},
&IPrimaryDevice::setTtyMode, &IPrimaryDevice::getTtyMode);
}
INSTANTIATE_TEST_CASE_P(TtyModeAccessorPrimaryHidl, TtyModeAccessorPrimaryHidlTest,
::testing::ValuesIn(getDeviceParameters()), &DeviceParameterToString);
TEST_F(BoolAccessorPrimaryHidlTest, setGetHac) {
TEST_P(BoolAccessorPrimaryHidlTest, setGetHac) {
doc::test("Query and set the HAC state");
testAccessors<OPTIONAL>("HAC", Initial{false}, {true}, &IPrimaryDevice::setHacEnabled,
&IPrimaryDevice::getHacEnabled);
@ -1372,9 +1504,13 @@ TEST_F(BoolAccessorPrimaryHidlTest, setGetHac) {
int main(int argc, char** argv) {
environment = new AudioHidlTestEnvironment;
// For V2..5 it's critical to initialize environment before GTest.
// The environment parses the service name from the command line,
// then it can be used in GTest parameter generators which are
// initialized during the call to InitGoogleTest.
environment->init(&argc, argv);
::testing::AddGlobalTestEnvironment(environment);
::testing::InitGoogleTest(&argc, argv);
environment->init(&argc, argv);
int status = RUN_ALL_TESTS();
return status;
}

View file

@ -0,0 +1,143 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
// Code in this file uses 'environment'
#ifndef AUDIO_PRIMARY_HIDL_HAL_TEST
#error Must be included from AudioPrimaryHidlTest.h
#endif
template <class Derived, class Key, class Interface>
class InterfaceManager {
public:
sp<Interface> get(const Key& name) {
auto existing = instances.find(name);
if (existing != instances.end()) return existing->second;
auto [inserted, _] = instances.emplace(name, Derived::createInterfaceInstance(name));
if (inserted->second) {
environment->registerTearDown([name]() { (void)Derived::getInstance().reset(name); });
}
return inserted->second;
}
// The test must check that reset was successful. Reset failure means that the test code
// is holding a strong reference to the device.
bool reset(const Key& name) __attribute__((warn_unused_result)) {
auto iter = instances.find(name);
if (iter == instances.end()) return true;
::android::wp<Interface> weak = iter->second;
instances.erase(iter);
if (weak.promote() != nullptr) return false;
waitForInstanceDestruction();
return true;
}
static void waitForInstanceDestruction() {
// FIXME: there is no way to know when the remote IDevice is being destroyed
// Binder does not support testing if an object is alive, thus
// wait for 100ms to let the binder destruction propagates and
// the remote device has the time to be destroyed.
// flushCommand makes sure all local command are sent, thus should reduce
// the latency between local and remote destruction.
IPCThreadState::self()->flushCommands();
usleep(100 * 1000);
}
protected:
std::map<Key, sp<Interface>> instances;
};
class DevicesFactoryManager
: public InterfaceManager<DevicesFactoryManager, std::string, IDevicesFactory> {
public:
static DevicesFactoryManager& getInstance() {
static DevicesFactoryManager instance;
return instance;
}
static sp<IDevicesFactory> createInterfaceInstance(const std::string& name) {
#if MAJOR_VERSION <= 5
return ::testing::VtsHalHidlTargetTestBase::getService<IDevicesFactory>(name);
#elif MAJOR_VERSION >= 6
return IDevicesFactory::getService(name);
#endif
}
};
using FactoryAndDevice = std::tuple<std::string, std::string>;
class DeviceManager : public InterfaceManager<DeviceManager, FactoryAndDevice, IDevice> {
public:
static DeviceManager& getInstance() {
static DeviceManager instance;
return instance;
}
static sp<IDevice> createInterfaceInstance(const FactoryAndDevice& factoryAndDevice) {
auto [factoryName, name] = factoryAndDevice;
sp<IDevicesFactory> factory = DevicesFactoryManager::getInstance().get(factoryName);
return name == kPrimaryDevice ? openPrimaryDevice(factory) : openDevice(factory, name);
}
using InterfaceManager::reset;
static constexpr const char* kPrimaryDevice = "primary";
sp<IDevice> get(const std::string& factoryName, const std::string& name) {
return InterfaceManager::get(std::make_tuple(factoryName, name));
}
sp<IPrimaryDevice> getPrimary(const std::string& factoryName) {
sp<IDevice> device = get(factoryName, kPrimaryDevice);
return device != nullptr ? IPrimaryDevice::castFrom(device) : nullptr;
}
bool reset(const std::string& factoryName, const std::string& name)
__attribute__((warn_unused_result)) {
return InterfaceManager::reset(std::make_tuple(factoryName, name));
}
bool resetPrimary(const std::string& factoryName) __attribute__((warn_unused_result)) {
return reset(factoryName, kPrimaryDevice);
}
private:
static sp<IDevice> openDevice(const sp<IDevicesFactory>& factory, const std::string& name) {
if (factory == nullptr) return nullptr;
sp<IDevice> device;
#if MAJOR_VERSION >= 4
Result result;
auto ret = factory->openDevice(name, returnIn(result, device));
if (!ret.isOk() || result != Result::OK || device == nullptr) {
ALOGW("Device %s can not be opened, transaction: %s, result %d, device %p",
name.c_str(), ret.description().c_str(), result, device.get());
return nullptr;
}
#else
(void)name;
#endif
return device;
}
static sp<IDevice> openPrimaryDevice(const sp<IDevicesFactory>& factory) {
if (factory == nullptr) return nullptr;
Result result;
sp<IDevice> device;
#if MAJOR_VERSION == 2
auto ret = factory->openDevice(IDevicesFactory::Device::PRIMARY, returnIn(result, device));
#elif MAJOR_VERSION >= 4
auto ret = factory->openPrimaryDevice(returnIn(result, device));
#endif
if (!ret.isOk() || result != Result::OK || device == nullptr) {
ALOGW("Primary device can not be opened, transaction: %s, result %d, device %p",
ret.description().c_str(), result, device.get());
return nullptr;
}
return device;
}
};