Merge "Extend configChange and tuneComplete callbacks with checking the carried value."
diff --git a/audio/2.0/vts/functional/Android.bp b/audio/2.0/vts/functional/Android.bp
new file mode 100644
index 0000000..05b1817
--- /dev/null
+++ b/audio/2.0/vts/functional/Android.bp
@@ -0,0 +1,38 @@
+//
+// Copyright (C) 2017 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.
+//
+
+cc_test {
+ name: "VtsHalAudioV2_0TargetTest",
+ gtest: true,
+ srcs: ["AudioPrimaryHidlHalTest.cpp"],
+ shared_libs: [
+ "libbase",
+ "liblog",
+ "libhidlbase",
+ "libutils",
+ "libcutils",
+ "android.hardware.audio@2.0",
+ "android.hardware.audio.common@2.0",
+ ],
+ static_libs: ["libgtest"],
+ cflags: [
+ "-O0",
+ "-g",
+ "-Wall",
+ "-Wextra",
+ "-Werror",
+ ],
+}
diff --git a/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
new file mode 100644
index 0000000..cc20456
--- /dev/null
+++ b/audio/2.0/vts/functional/AudioPrimaryHidlHalTest.cpp
@@ -0,0 +1,708 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#define LOG_TAG "VtsHalAudioV2_0TargetTest"
+
+#include <algorithm>
+#include <cmath>
+#include <cstddef>
+#include <cstdio>
+#include <limits>
+#include <list>
+#include <string>
+#include <type_traits>
+#include <vector>
+
+#include <gtest/gtest.h>
+
+#include <android-base/logging.h>
+
+#include <android/hardware/audio/2.0/IDevice.h>
+#include <android/hardware/audio/2.0/IDevicesFactory.h>
+#include <android/hardware/audio/2.0/types.h>
+#include <android/hardware/audio/common/2.0/types.h>
+
+#include "utility/ReturnIn.h"
+#include "utility/AssertOk.h"
+#include "utility/PrettyPrintAudioTypes.h"
+
+using std::string;
+using std::to_string;
+using std::vector;
+
+using ::android::sp;
+using ::android::hardware::Return;
+using ::android::hardware::hidl_handle;
+using ::android::hardware::hidl_string;
+using ::android::hardware::hidl_vec;
+using ::android::hardware::audio::V2_0::DeviceAddress;
+using ::android::hardware::audio::V2_0::IDevice;
+using ::android::hardware::audio::V2_0::IDevicesFactory;
+using ::android::hardware::audio::V2_0::IStream;
+using ::android::hardware::audio::V2_0::IStreamIn;
+using ::android::hardware::audio::V2_0::IStreamOut;
+using ::android::hardware::audio::V2_0::ParameterValue;
+using ::android::hardware::audio::V2_0::Result;
+using ::android::hardware::audio::common::V2_0::AudioChannelMask;
+using ::android::hardware::audio::common::V2_0::AudioConfig;
+using ::android::hardware::audio::common::V2_0::AudioDevice;
+using ::android::hardware::audio::common::V2_0::AudioFormat;
+using ::android::hardware::audio::common::V2_0::AudioHandleConsts;
+using ::android::hardware::audio::common::V2_0::AudioInputFlag;
+using ::android::hardware::audio::common::V2_0::AudioIoHandle;
+using ::android::hardware::audio::common::V2_0::AudioOffloadInfo;
+using ::android::hardware::audio::common::V2_0::AudioOutputFlag;
+using ::android::hardware::audio::common::V2_0::AudioSource;
+
+using utility::returnIn;
+
+namespace doc {
+/** Document the current test case.
+ * Eg: calling `doc::test("Dump the state of the hal")` in the "debugDump" test will output:
+ * <testcase name="debugDump" status="run" time="6" classname="AudioPrimaryHidlTest"
+ description="Dump the state of the hal." />
+ * see https://github.com/google/googletest/blob/master/googletest/docs/AdvancedGuide.md#logging-additional-information
+ */
+void test(const std::string& testCaseDocumentation) {
+ ::testing::Test::RecordProperty("description", testCaseDocumentation);
+}
+
+/** Document why a test was not fully run. Usually due to an optional feature not implemented. */
+void partialTest(const std::string& reason) {
+ ::testing::Test::RecordProperty("partialyRunTest", reason);
+}
+}
+
+// Register callback for static object destruction
+// 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::Environment {
+public:
+ using TearDownFunc = std::function<void ()>;
+ void registerTearDown(TearDownFunc&& tearDown) {
+ tearDowns.push_back(std::move(tearDown));
+ }
+
+private:
+ void TearDown() override {
+ // Call the tear downs in reverse order of insertion
+ for (auto& tearDown : tearDowns) {
+ tearDown();
+ }
+ }
+ std::list<TearDownFunc> tearDowns;
+};
+// Instance to register global tearDown
+static Environment* environment;
+
+class HidlTest : public ::testing::Test {
+protected:
+ // Convenient member to store results
+ Result res;
+};
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////// getService audio_devices_factory //////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// Test all audio devices
+class AudioHidlTest : public HidlTest {
+public:
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(HidlTest::SetUp()); // setup base
+
+ if (devicesFactory == nullptr) {
+ environment->registerTearDown([]{ devicesFactory.clear(); });
+ devicesFactory = IDevicesFactory::getService();
+ }
+ ASSERT_TRUE(devicesFactory != nullptr);
+ }
+
+protected:
+ // Cache the devicesFactory retrieval to speed up each test by ~0.5s
+ static sp<IDevicesFactory> devicesFactory;
+};
+sp<IDevicesFactory> AudioHidlTest::devicesFactory;
+
+TEST_F(AudioHidlTest, GetAudioDevicesFactoryService) {
+ doc::test("test the getService (called in SetUp)");
+}
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// openDevice primary ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// Test the primary device
+class AudioPrimaryHidlTest : public AudioHidlTest {
+public:
+ /** Primary HAL test are NOT thread safe. */
+ void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(AudioHidlTest::SetUp()); // setup base
+
+ if (device == nullptr) {
+ environment->registerTearDown([]{ device.clear(); });
+ IDevicesFactory::Result result;
+ ASSERT_OK(devicesFactory->openDevice(IDevicesFactory::Device::PRIMARY,
+ returnIn(result, device)));
+ }
+ ASSERT_TRUE(device != nullptr);
+ }
+
+protected:
+ // Cache the device opening to speed up each test by ~0.5s
+ static sp<IDevice> device;
+};
+sp<IDevice> AudioPrimaryHidlTest::device;
+
+TEST_F(AudioPrimaryHidlTest, OpenPrimaryDevice) {
+ doc::test("Test the openDevice (called in SetUp)");
+}
+
+TEST_F(AudioPrimaryHidlTest, Init) {
+ doc::test("Test that the audio primary hal initialized correctly");
+ ASSERT_OK(device->initCheck());
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {set,get}MasterVolume ///////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class MasterVolumeTest : public AudioPrimaryHidlTest {
+protected:
+ void testSetGetConsistency(float volume, Result expectedSetResult, float expectedGetVolume) {
+ SCOPED_TRACE("Test set/get consistency for " + to_string(volume));
+ auto ret = device->setMasterVolume(volume);
+ ASSERT_TRUE(ret.isOk());
+ ASSERT_EQ(expectedSetResult, ret);
+
+ float observedVolume;
+ ASSERT_OK(device->getMasterVolume(returnIn(res, observedVolume)));
+ ASSERT_OK(res);
+
+ // Check that `get` returned the expected value
+ EXPECT_EQ(expectedGetVolume, observedVolume);
+ }
+};
+
+TEST_F(MasterVolumeTest, MasterVolumeTest) {
+ doc::test("Test the master volume if supported");
+ {
+ SCOPED_TRACE("Check for master volume support");
+ auto ret = device->setMasterVolume(1);
+ ASSERT_TRUE(ret.isOk());
+ if (ret == Result::NOT_SUPPORTED) {
+ doc::partialTest("Master volume is not supported");
+ return;
+ }
+ }
+ // NOTE: this code has never been tested on a platform supporting MasterVolume
+ float lastValidVolumeSet;
+ using Volumes = float[];
+ SCOPED_TRACE("As set/get master volume are supported...");
+ {
+ SCOPED_TRACE("...test them with valid values");
+ for (float validVolume : Volumes{0, 0.5, 1}) {
+ ASSERT_NO_FATAL_FAILURE(testSetGetConsistency(validVolume, Result::OK, validVolume));
+ lastValidVolumeSet = validVolume;
+ }
+ }{
+ SCOPED_TRACE("...test them with tricky values");
+ for (float outOfBoundVolume :Volumes{-0.1, 1.1, NAN, INFINITY, -INFINITY,
+ 1 + std::numeric_limits<float>::epsilon()}) {
+ ASSERT_NO_FATAL_FAILURE(testSetGetConsistency(outOfBoundVolume,
+ Result::INVALID_ARGUMENTS,
+ lastValidVolumeSet));
+ }
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// {set,get}{Master,Mic}Mute /////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+class BoolAccessorPrimaryHidlTest : public AudioPrimaryHidlTest {
+protected:
+ template <class Getter, class Setter>
+ void testBoolAccessors(const string& propertyName, const vector<bool>& valuesToTest,
+ Setter setter, Getter getter) {
+ for (bool setState : valuesToTest) {
+ SCOPED_TRACE("Test " + propertyName + " state: " + to_string(setState));
+ ASSERT_OK((device.get()->*setter)(setState));
+ bool getState;
+ ASSERT_OK((device.get()->*getter)(returnIn(res, getState)));
+ ASSERT_OK(res);
+ ASSERT_EQ(setState, getState);
+ }
+ }
+};
+
+TEST_F(BoolAccessorPrimaryHidlTest, MicMuteTest) {
+ doc::test("Check that the mic can be muted and unmuted");
+ testBoolAccessors("mic mute", {true, false, true}, &IDevice::setMicMute, &IDevice::getMicMute);
+ // TODO: check that the mic is really muted (all sample are 0)
+}
+
+TEST_F(BoolAccessorPrimaryHidlTest, MasterMuteTest) {
+ doc::test("If master mute is supported, try to mute and unmute the master output");
+ {
+ SCOPED_TRACE("Check for master mute support");
+ auto ret = device->setMasterMute(false);
+ ASSERT_TRUE(ret.isOk());
+ if (ret == Result::NOT_SUPPORTED) {
+ doc::partialTest("Master mute is not supported");
+ return;
+ }
+ }
+ // NOTE: this code has never been tested on a platform supporting MasterMute
+ testBoolAccessors("master mute", {true, false, true},
+ &IDevice::setMasterMute, &IDevice::getMasterMute);
+ // TODO: check that the master volume is really muted
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////// Required and recommended audio format support ///////////////
+// From: https://source.android.com/compatibility/android-cdd.html#5_4_audio_recording
+// From: https://source.android.com/compatibility/android-cdd.html#5_5_audio_playback
+/////////// TODO: move to the beginning of the file for easier update ////////
+//////////////////////////////////////////////////////////////////////////////
+
+class AudioConfigPrimaryTest : public AudioPrimaryHidlTest {
+public:
+ // Cache result ?
+ static const vector<AudioConfig> getRequiredSupportPlaybackAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
+ {8000, 11025, 16000, 22050, 32000, 44100},
+ {AudioFormat::PCM_16_BIT});
+ }
+
+ static const vector<AudioConfig> getRecommendedSupportPlaybackAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::OUT_STEREO, AudioChannelMask::OUT_MONO},
+ {24000, 48000},
+ {AudioFormat::PCM_16_BIT});
+ }
+
+ static const vector<AudioConfig> getSupportedPlaybackAudioConfig() {
+ // TODO: retrieve audio config supported by the platform
+ // as declared in the policy configuration
+ return {};
+ }
+
+ static const vector<AudioConfig> getRequiredSupportCaptureAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::IN_MONO},
+ {8000, 11025, 16000, 44100},
+ {AudioFormat::PCM_16_BIT});
+ }
+ static const vector<AudioConfig> getRecommendedSupportCaptureAudioConfig() {
+ return combineAudioConfig({AudioChannelMask::IN_STEREO},
+ {22050, 48000},
+ {AudioFormat::PCM_16_BIT});
+ }
+ static const vector<AudioConfig> getSupportedCaptureAudioConfig() {
+ // TODO: retrieve audio config supported by the platform
+ // as declared in the policy configuration
+ return {};
+ }
+private:
+ static const vector<AudioConfig> combineAudioConfig(
+ vector<AudioChannelMask> channelMasks,
+ vector<uint32_t> sampleRates,
+ vector<AudioFormat> formats) {
+ vector<AudioConfig> configs;
+ for (auto channelMask: channelMasks) {
+ for (auto sampleRate : sampleRates) {
+ for (auto format : formats) {
+ AudioConfig config{};
+ // leave offloadInfo to 0
+ config.channelMask = channelMask;
+ config.sampleRateHz = sampleRate;
+ config.format = format;
+ // FIXME: leave frameCount to 0 ?
+ configs.push_back(config);
+ }
+ }
+ }
+ return configs;
+ }
+};
+
+//////////////////////////////////////////////////////////////////////////////
+///////////////////////////// getInputBufferSize /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+// FIXME: execute input test only if platform declares android.hardware.microphone
+// how to get this value ? is it a property ???
+
+class AudioCaptureConfigPrimaryTest : public AudioConfigPrimaryTest,
+ public ::testing::WithParamInterface<AudioConfig> {
+protected:
+ void inputBufferSizeTest(const AudioConfig& audioConfig, bool supportRequired) {
+ uint64_t bufferSize;
+ ASSERT_OK(device->getInputBufferSize(audioConfig, returnIn(res, bufferSize)));
+
+ switch (res) {
+ case Result::INVALID_ARGUMENTS:
+ EXPECT_FALSE(supportRequired);
+ break;
+ case Result::OK:
+ // Check that the buffer is of a sane size
+ // For now only that it is > 0
+ EXPECT_GT(bufferSize, uint64_t(0));
+ break;
+ default:
+ FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+ }
+ }
+};
+
+// Test that the required capture config and those declared in the policy are indeed supported
+class RequiredInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {};
+TEST_P(RequiredInputBufferSizeTest, RequiredInputBufferSizeTest) {
+ doc::test("Input buffer size must be retrievable for a format with required support.");
+ inputBufferSizeTest(GetParam(), true);
+}
+INSTANTIATE_TEST_CASE_P(
+ RequiredInputBufferSize, RequiredInputBufferSizeTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+ SupportedInputBufferSize, RequiredInputBufferSizeTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()));
+
+// Test that the recommended capture config are supported or lead to a INVALID_ARGUMENTS return
+class OptionalInputBufferSizeTest : public AudioCaptureConfigPrimaryTest {};
+TEST_P(OptionalInputBufferSizeTest, OptionalInputBufferSizeTest) {
+ doc::test("Input buffer size should be retrievable for a format with recommended support.");
+ inputBufferSizeTest(GetParam(), false);
+}
+INSTANTIATE_TEST_CASE_P(
+ RecommendedCaptureAudioConfigSupport, OptionalInputBufferSizeTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()));
+
+//////////////////////////////////////////////////////////////////////////////
+/////////////////////////////// setScreenState ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, setScreenState) {
+ doc::test("Check that the hal can receive the screen state");
+ for (bool turnedOn : {false, true, true, false, false}) {
+ auto ret = device->setScreenState(turnedOn);
+ ASSERT_TRUE(ret.isOk());
+ Result result = ret;
+ ASSERT_TRUE(result == Result::OK || result == Result::NOT_SUPPORTED) << toString(result);
+ }
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////// {get,set}Parameters /////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, getParameters) {
+ doc::test("Check that the hal can set and get parameters");
+ hidl_vec<hidl_string> keys;
+ hidl_vec<ParameterValue> values;
+ ASSERT_OK(device->getParameters(keys, returnIn(res, values)));
+ ASSERT_OK(device->setParameters(values));
+ values.resize(0);
+ ASSERT_OK(device->setParameters(values));
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// debugDebug //////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+TEST_F(AudioPrimaryHidlTest, debugDump) {
+ doc::test("Check that the hal can dump its state without error");
+ FILE* file = tmpfile();
+ ASSERT_NE(nullptr, file) << errno;
+
+ auto* nativeHandle = native_handle_create(1, 0);
+ ASSERT_NE(nullptr, nativeHandle);
+ nativeHandle->data[0] = fileno(file);
+
+ hidl_handle handle;
+ handle.setTo(nativeHandle, true /*take ownership*/);
+
+ auto ret = device->debugDump(handle);
+ ASSERT_TRUE(ret.isOk());
+
+ // FIXME: debugDump does not return a Result.
+ // This mean that the hal can not report that it not implementing the function.
+ // As the default hal does not implement debugDump, the function can not be tested.
+ /*
+ res = ret;
+ if (res == Result::NOT_SUPPORTED) {
+ doc::partialTest("debugDump is not implemented");
+ return;
+ }
+ ASSERT_OK(res);
+ rewind(file);
+
+ // Check that at least one bit was written by the hal
+ char buff;
+ ASSERT_EQ(1, fread(&buff, sizeof(buff), 1, file));
+ */
+ EXPECT_EQ(0, fclose(file)) << errno;
+}
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////// open{Output,Input}Stream //////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+template <class Stream>
+class OpenStreamTest : public AudioConfigPrimaryTest,
+ public ::testing::WithParamInterface<AudioConfig> {
+protected:
+ template <class Open>
+ void testOpen(Open openStream, const AudioConfig& config) {
+ // FIXME: Open a stream without an IOHandle
+ // This is not required to be accepted by hal implementations
+ AudioIoHandle ioHandle = (AudioIoHandle)AudioHandleConsts::AUDIO_IO_HANDLE_NONE;
+ AudioConfig suggestedConfig{};
+ ASSERT_OK(openStream(ioHandle, config, returnIn(res, stream, suggestedConfig)));
+
+ // TODO: only allow failure for RecommendedPlaybackAudioConfig
+ switch (res) {
+ case Result::OK:
+ ASSERT_TRUE(stream != nullptr);
+ audioConfig = config;
+ break;
+ case Result::INVALID_ARGUMENTS:
+ ASSERT_TRUE(stream == nullptr);
+ AudioConfig suggestedConfigRetry;
+ // Could not open stream with config, try again with the suggested one
+ ASSERT_OK(openStream(ioHandle, suggestedConfig,
+ returnIn(res, stream, suggestedConfigRetry)));
+ // This time it must succeed
+ ASSERT_OK(res);
+ ASSERT_TRUE(stream == nullptr);
+ audioConfig = suggestedConfig;
+ break;
+ default:
+ FAIL() << "Invalid return status: " << ::testing::PrintToString(res);
+ }
+ open = true;
+ }
+
+private:
+ void TearDown() override {
+ if (open) {
+ ASSERT_OK(stream->close());
+ }
+ }
+
+protected:
+
+ AudioConfig audioConfig;
+ sp<Stream> stream;
+ bool open = false;
+};
+
+////////////////////////////// openOutputStream //////////////////////////////
+
+class OutputStreamTest : public OpenStreamTest<IStreamOut> {
+ virtual void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
+
+ const AudioConfig& config = GetParam();
+ DeviceAddress deviceAddr{}; // Ignored by primary hal
+ AudioOutputFlag flags = AudioOutputFlag::NONE; // TODO: test all flag combination
+ testOpen([&](AudioIoHandle handle, AudioConfig config, auto cb)
+ { return device->openOutputStream(handle, deviceAddr, config, flags, cb); },
+ config);
+ }
+};
+TEST_P(OutputStreamTest, OpenOutputStreamTest) {
+ doc::test("Check that output streams can be open with the required and recommended config");
+ // Open done in SetUp
+}
+INSTANTIATE_TEST_CASE_P(
+ RequiredOutputStreamConfigSupport, OutputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportPlaybackAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+ SupportedOutputStreamConfig, OutputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedPlaybackAudioConfig()));
+
+INSTANTIATE_TEST_CASE_P(
+ RecommendedOutputStreamConfigSupport, OutputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportPlaybackAudioConfig()));
+
+////////////////////////////// openInputStream //////////////////////////////
+
+class InputStreamTest : public OpenStreamTest<IStreamIn> {
+
+ virtual void SetUp() override {
+ ASSERT_NO_FATAL_FAILURE(OpenStreamTest::SetUp()); // setup base
+
+ const AudioConfig& config = GetParam();
+ DeviceAddress deviceAddr{}; // TODO: test all devices
+ AudioInputFlag flags = AudioInputFlag::NONE; // TODO: test all flag combination
+ AudioSource source = AudioSource::DEFAULT; // TODO: test all flag combination
+ testOpen([&](AudioIoHandle handle, AudioConfig config, auto cb)
+ { return device->openInputStream(handle, deviceAddr, config, flags, source, cb); },
+ config);
+ }
+};
+
+TEST_P(InputStreamTest, OpenInputStreamTest) {
+ doc::test("Check that input streams can be open with the required and recommended config");
+ // Open done in setup
+}
+INSTANTIATE_TEST_CASE_P(
+ RequiredInputStreamConfigSupport, InputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRequiredSupportCaptureAudioConfig()));
+INSTANTIATE_TEST_CASE_P(
+ SupportedInputStreamConfig, InputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getSupportedCaptureAudioConfig()));
+
+INSTANTIATE_TEST_CASE_P(
+ RecommendedInputStreamConfigSupport, InputStreamTest,
+ ::testing::ValuesIn(AudioConfigPrimaryTest::getRecommendedSupportCaptureAudioConfig()));
+
+//////////////////////////////////////////////////////////////////////////////
+////////////////////////////// IStream getters ///////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+/** Unpack the provided result.
+ * If the result is not OK, register a failure and return an undefined value. */
+template <class R>
+static R extract(Return<R> ret) {
+ if (!ret.isOk()) {
+ ADD_FAILURE();
+ return R{};
+ }
+ return ret;
+}
+
+template <class Property, class CapabilityGetter, class Getter, class Setter>
+static void testCapabilityGetter(const string& name,IStream* stream, Property currentValue,
+ CapabilityGetter capablityGetter, Getter getter, Setter setter) {
+ hidl_vec<Property> capabilities;
+ ASSERT_OK((stream->*capablityGetter)(returnIn(capabilities)));
+ if (capabilities.size() == 0) {
+ // The default hal should probably return a NOT_SUPPORTED if the hal does not expose
+ // capability retrieval. For now it returns an empty list if not implemented
+ doc::partialTest(name + " is not supported");
+ return;
+ };
+ // TODO: This code has never been tested on a hal that supports getSupportedSampleRates
+ EXPECT_NE(std::find(capabilities.begin(), capabilities.end(), currentValue),
+ capabilities.end())
+ << "current " << name << " is not in the list of the supported ones "
+ << toString(capabilities);
+
+ // Check that all declared supported values are indeed supported
+ for (auto capability : capabilities) {
+ ASSERT_OK((stream->*setter)(capability));
+ ASSERT_EQ(capability, extract((stream->*getter)()));
+ }
+}
+
+static void testGetAudioProperties(IStream* stream, AudioConfig expectedConfig) {
+ uint32_t sampleRateHz;
+ AudioChannelMask mask;
+ AudioFormat format;
+
+ stream->getAudioProperties(returnIn(sampleRateHz, mask, format));
+
+ // FIXME: the qcom hal it does not currently negotiate the sampleRate & channel mask
+ // EXPECT_EQ(expectedConfig.sampleRateHz, sampleRateHz);
+ // EXPECT_EQ(expectedConfig.channelMask, mask);
+ EXPECT_EQ(expectedConfig.format, format);
+}
+
+static void testAccessors(IStream* stream, AudioConfig audioConfig) {
+ doc::test("Test IStream getters and setters that can be called in the stop state");
+
+ auto frameCount = extract(stream->getFrameCount());
+ ASSERT_EQ(audioConfig.frameCount, frameCount);
+
+ auto sampleRate = extract(stream->getSampleRate());
+ // FIXME: the qcom hal it does not currently negotiate the sampleRate
+ // ASSERT_EQ(audioConfig.sampleRateHz, sampleRate);
+
+ auto channelMask = extract(stream->getChannelMask());
+ // FIXME: the qcom hal it does not currently negotiate the channelMask
+ // ASSERT_EQ(audioConfig.channelMask, channelMask);
+
+ auto frameSize = extract(stream->getFrameSize());
+ ASSERT_GE(frameSize, 0U);
+
+ auto bufferSize = extract(stream->getBufferSize());
+ ASSERT_GE(bufferSize, frameSize);
+
+ testCapabilityGetter("getSupportedsampleRate", stream, sampleRate,
+ &IStream::getSupportedSampleRates,
+ &IStream::getSampleRate, &IStream::setSampleRate);
+
+ testCapabilityGetter("getSupportedChannelMask", stream, channelMask,
+ &IStream::getSupportedChannelMasks,
+ &IStream::getChannelMask, &IStream::setChannelMask);
+
+ AudioFormat format = extract(stream->getFormat());
+ ASSERT_EQ(audioConfig.format, format);
+
+ testCapabilityGetter("getSupportedFormats", stream, format,
+ &IStream::getSupportedFormats, &IStream::getFormat, &IStream::setFormat);
+
+ testGetAudioProperties(stream, audioConfig);
+
+ // FIXME: Stream wrapper does not implement getDevice properly.
+ // It needs to call getProperty({"routing"}).
+ // The current implementation segfault with the default hal
+ /*
+ * auto ret = stream->getDevice();
+ * ASSERT_TRUE(ret.isOk());
+ * AudioDevice device = ret;
+ * ASSERT_EQ(AudioDevice::OUT_ALL, device);
+ */
+}
+
+TEST_P(InputStreamTest, GettersTest) {
+ testAccessors(stream.get(), audioConfig);
+}
+TEST_P(OutputStreamTest, GettersTest) {
+ testAccessors(stream.get(), audioConfig);
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////////////////// AudioPatches ////////////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+
+TEST_F(AudioPrimaryHidlTest, AudioPatches) {
+ doc::test("Test if audio patches are supported");
+ auto result = device->supportsAudioPatches();
+ ASSERT_TRUE(result.isOk());
+ bool supportsAudioPatch = result;
+ if (!supportsAudioPatch) {
+ doc::partialTest("Audio patches are not supported");
+ return;
+ }
+ // TODO: test audio patches
+}
+
+//////////////////////////////////////////////////////////////////////////////
+//////////////////// Clean caches on global tear down ////////////////////////
+//////////////////////////////////////////////////////////////////////////////
+
+int main(int argc, char** argv) {
+ environment = new Environment;
+ ::testing::AddGlobalTestEnvironment(environment);
+ ::testing::InitGoogleTest(&argc, argv);
+ int status = RUN_ALL_TESTS();
+ LOG(INFO) << "Test result = " << status;
+ return status;
+}
diff --git a/audio/2.0/vts/functional/utility/AssertOk.h b/audio/2.0/vts/functional/utility/AssertOk.h
new file mode 100644
index 0000000..5397436
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/AssertOk.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+#include <hidl/Status.h>
+
+namespace detail {
+
+inline void assertOk(::android::hardware::Return<void> ret) {
+ ASSERT_TRUE(ret.isOk());
+}
+
+inline void assertOk(::android::hardware::audio::V2_0::Result result) {
+ ASSERT_EQ(decltype(result)::OK, result);
+}
+
+inline void assertOk(::android::hardware::Return<::android::hardware::audio::V2_0::Result> ret) {
+ ASSERT_TRUE(ret.isOk());
+ ::android::hardware::audio::V2_0::Result result = ret;
+ assertOk(result);
+}
+
+}
+
+// Test anything provided is and contains only OK
+#define ASSERT_OK(ret) ASSERT_NO_FATAL_FAILURE(detail::assertOk(ret))
+#define EXPECT_OK(ret) EXPECT_NO_FATAL_FAILURE(detail::assertOk(ret))
diff --git a/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h b/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h
new file mode 100644
index 0000000..8dfcb29
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/PrettyPrintAudioTypes.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+// Use HIDL generated toString methods to pretty print gtest errors
+namespace android {
+namespace hardware {
+namespace audio {
+namespace V2_0 {
+inline void PrintTo(const Result& result, ::std::ostream* os) {
+ *os << toString(result);
+}
+} // namespace V2_0
+namespace common {
+namespace V2_0 {
+inline void PrintTo(const AudioConfig& config, ::std::ostream* os) {
+ *os << toString(config);
+}
+} // namespace V2_0
+} // namespace common
+}
+}
+}
diff --git a/audio/2.0/vts/functional/utility/ReturnIn.h b/audio/2.0/vts/functional/utility/ReturnIn.h
new file mode 100644
index 0000000..bb2389a
--- /dev/null
+++ b/audio/2.0/vts/functional/utility/ReturnIn.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include <tuple>
+
+namespace utility {
+
+namespace detail {
+// Helper class to generate the HIDL synchronous callback
+template <class... ResultStore>
+class ReturnIn {
+ public:
+ // Provide to the constructor the variables where the output parameters must be copied
+ // TODO: take pointers to match google output parameter style ?
+ ReturnIn(ResultStore&... ts) : results(ts...) {}
+ // Synchronous callback
+ template <class... Results>
+ void operator() (Results&&...results) {
+ set(std::forward<Results>(results)...);
+ }
+ private:
+ // Recursively set all output parameters
+ template <class Head, class... Tail>
+ void set(Head&& head, Tail&&... tail) {
+ std::get<sizeof...(ResultStore) - sizeof...(Tail) - 1>(results)
+ = std::forward<Head>(head);
+ set(tail...);
+ }
+ // Trivial case
+ void set() {}
+
+ // All variables to set are stored here
+ std::tuple<ResultStore&...> results;
+};
+} // namespace detail
+
+// Generate the HIDL synchronous callback with a copy policy
+// Input: the variables (lvalue reference) where to save the return values
+// Output: the callback to provide to a HIDL call with a synchronous callback
+// The output parameters *will be copied* do not use this function if you have
+// a zero copy policy
+template <class... ResultStore>
+detail::ReturnIn<ResultStore...> returnIn(ResultStore&... ts) { return {ts...};}
+
+}
diff --git a/audio/Android.bp b/audio/Android.bp
index 3121a36..8a1e892 100644
--- a/audio/Android.bp
+++ b/audio/Android.bp
@@ -1,6 +1,7 @@
// This is an autogenerated file, do not edit.
subdirs = [
"2.0",
+ "2.0/vts/functional",
"common/2.0",
"effect/2.0",
"effect/2.0/vts/functional",
diff --git a/bluetooth/1.0/default/async_fd_watcher.cc b/bluetooth/1.0/default/async_fd_watcher.cc
index 287d007..2f23a69 100644
--- a/bluetooth/1.0/default/async_fd_watcher.cc
+++ b/bluetooth/1.0/default/async_fd_watcher.cc
@@ -19,6 +19,7 @@
#include <algorithm>
#include <atomic>
#include <condition_variable>
+#include <map>
#include <mutex>
#include <thread>
#include <vector>
@@ -26,6 +27,8 @@
#include "sys/select.h"
#include "unistd.h"
+static const int INVALID_FD = -1;
+
namespace android {
namespace hardware {
namespace bluetooth {
@@ -36,8 +39,7 @@
// Add file descriptor and callback
{
std::unique_lock<std::mutex> guard(internal_mutex_);
- read_fd_ = file_descriptor;
- cb_ = on_read_fd_ready_callback;
+ watched_fds_[file_descriptor] = on_read_fd_ready_callback;
}
// Start the thread if not started yet
@@ -58,7 +60,7 @@
return 0;
}
-void AsyncFdWatcher::StopWatchingFileDescriptor() { stopThread(); }
+void AsyncFdWatcher::StopWatchingFileDescriptors() { stopThread(); }
AsyncFdWatcher::~AsyncFdWatcher() {}
@@ -90,8 +92,7 @@
{
std::unique_lock<std::mutex> guard(internal_mutex_);
- cb_ = nullptr;
- read_fd_ = -1;
+ watched_fds_.clear();
}
{
@@ -115,7 +116,11 @@
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(notification_listen_fd_, &read_fds);
- FD_SET(read_fd_, &read_fds);
+ int max_read_fd = INVALID_FD;
+ for (auto& it : watched_fds_) {
+ FD_SET(it.first, &read_fds);
+ max_read_fd = std::max(max_read_fd, it.first);
+ }
struct timeval timeout;
struct timeval* timeout_ptr = NULL;
@@ -126,7 +131,7 @@
}
// Wait until there is data available to read on some FD.
- int nfds = std::max(notification_listen_fd_, read_fd_);
+ int nfds = std::max(notification_listen_fd_, max_read_fd);
int retval = select(nfds + 1, &read_fds, NULL, NULL, timeout_ptr);
// There was some error.
@@ -153,10 +158,21 @@
continue;
}
- // Invoke the data ready callback if appropriate.
- if (FD_ISSET(read_fd_, &read_fds)) {
+ // Invoke the data ready callbacks if appropriate.
+ std::vector<decltype(watched_fds_)::value_type> saved_callbacks;
+ {
std::unique_lock<std::mutex> guard(internal_mutex_);
- if (cb_) cb_(read_fd_);
+ for (auto& it : watched_fds_) {
+ if (FD_ISSET(it.first, &read_fds)) {
+ saved_callbacks.push_back(it);
+ }
+ }
+ }
+
+ for (auto& it : saved_callbacks) {
+ if (it.second) {
+ it.second(it.first);
+ }
}
}
}
diff --git a/bluetooth/1.0/default/async_fd_watcher.h b/bluetooth/1.0/default/async_fd_watcher.h
index 3f7ff54..358cbc3 100644
--- a/bluetooth/1.0/default/async_fd_watcher.h
+++ b/bluetooth/1.0/default/async_fd_watcher.h
@@ -16,6 +16,7 @@
#pragma once
+#include <map>
#include <mutex>
#include <thread>
@@ -36,7 +37,7 @@
const ReadCallback& on_read_fd_ready_callback);
int ConfigureTimeout(const std::chrono::milliseconds timeout,
const TimeoutCallback& on_timeout_callback);
- void StopWatchingFileDescriptor();
+ void StopWatchingFileDescriptors();
private:
AsyncFdWatcher(const AsyncFdWatcher&) = delete;
@@ -52,10 +53,9 @@
std::mutex internal_mutex_;
std::mutex timeout_mutex_;
- int read_fd_;
+ std::map<int, ReadCallback> watched_fds_;
int notification_listen_fd_;
int notification_write_fd_;
- ReadCallback cb_;
TimeoutCallback timeout_cb_;
std::chrono::milliseconds timeout_ms_;
};
diff --git a/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc b/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
index a7f5bda..b0c533c 100644
--- a/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
+++ b/bluetooth/1.0/default/test/async_fd_watcher_unittest.cc
@@ -14,6 +14,8 @@
// limitations under the License.
//
+#define LOG_TAG "async_fd_watcher_unittest"
+
#include "async_fd_watcher.h"
#include <gtest/gtest.h>
#include <cstdint>
@@ -122,8 +124,8 @@
}
void CleanUpServer() {
- async_fd_watcher_.StopWatchingFileDescriptor();
- conn_watcher_.StopWatchingFileDescriptor();
+ async_fd_watcher_.StopWatchingFileDescriptors();
+ conn_watcher_.StopWatchingFileDescriptors();
close(socket_fd_);
}
@@ -211,7 +213,7 @@
});
ConnectClient();
- conn_watcher.StopWatchingFileDescriptor();
+ conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
@@ -233,7 +235,7 @@
EXPECT_FALSE(timed_out);
sleep(1);
EXPECT_TRUE(timed_out);
- conn_watcher.StopWatchingFileDescriptor();
+ conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
@@ -265,10 +267,64 @@
sleep(1);
EXPECT_TRUE(timed_out);
EXPECT_TRUE(timed_out2);
- conn_watcher.StopWatchingFileDescriptor();
+ conn_watcher.StopWatchingFileDescriptors();
close(socket_fd);
}
+// Use a single AsyncFdWatcher to watch two file descriptors.
+TEST_F(AsyncFdWatcherSocketTest, WatchTwoFileDescriptors) {
+ int sockfd[2];
+ socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);
+ bool cb1_called = false;
+ bool* cb1_called_ptr = &cb1_called;
+ bool cb2_called = false;
+ bool* cb2_called_ptr = &cb2_called;
+
+ AsyncFdWatcher watcher;
+ watcher.WatchFdForNonBlockingReads(sockfd[0], [cb1_called_ptr](int fd) {
+ char read_buf[1] = {0};
+ int n = TEMP_FAILURE_RETRY(read(fd, read_buf, sizeof(read_buf)));
+ ASSERT_TRUE(n == sizeof(read_buf));
+ ASSERT_TRUE(read_buf[0] == '1');
+ *cb1_called_ptr = true;
+ });
+
+ watcher.WatchFdForNonBlockingReads(sockfd[1], [cb2_called_ptr](int fd) {
+ char read_buf[1] = {0};
+ int n = TEMP_FAILURE_RETRY(read(fd, read_buf, sizeof(read_buf)));
+ ASSERT_TRUE(n == sizeof(read_buf));
+ ASSERT_TRUE(read_buf[0] == '2');
+ *cb2_called_ptr = true;
+ });
+
+ // Fail if the test doesn't pass within 3 seconds
+ watcher.ConfigureTimeout(std::chrono::seconds(3), [this]() {
+ bool connection_timeout = true;
+ ASSERT_FALSE(connection_timeout);
+ });
+
+ EXPECT_FALSE(cb1_called);
+ EXPECT_FALSE(cb2_called);
+
+ char one_buf[1] = {'1'};
+ TEMP_FAILURE_RETRY(write(sockfd[1], one_buf, sizeof(one_buf)));
+
+ sleep(1);
+
+ EXPECT_TRUE(cb1_called);
+ EXPECT_FALSE(cb2_called);
+
+ char two_buf[1] = {'2'};
+ TEMP_FAILURE_RETRY(write(sockfd[0], two_buf, sizeof(two_buf)));
+
+ sleep(1);
+
+ EXPECT_TRUE(cb1_called);
+ EXPECT_TRUE(cb2_called);
+
+ watcher.StopWatchingFileDescriptors();
+}
+
// Use two AsyncFdWatchers to set up a server socket.
TEST_F(AsyncFdWatcherSocketTest, ClientServer) {
ConfigureServer();
diff --git a/bluetooth/1.0/default/vendor_interface.cc b/bluetooth/1.0/default/vendor_interface.cc
index 3878129..2c55d1c 100644
--- a/bluetooth/1.0/default/vendor_interface.cc
+++ b/bluetooth/1.0/default/vendor_interface.cc
@@ -274,7 +274,7 @@
}
void VendorInterface::Close() {
- fd_watcher_.StopWatchingFileDescriptor();
+ fd_watcher_.StopWatchingFileDescriptors();
if (lib_interface_ != nullptr) {
bt_vendor_lpm_mode_t mode = BT_VND_LPM_DISABLE;
diff --git a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
index 5b6089d..4aa6d7e 100644
--- a/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
+++ b/nfc/1.0/vts/functional/VtsHalNfcV1_0TargetTest.cpp
@@ -325,18 +325,42 @@
}
/*
+ * PowerCycleAfterClose:
+ * Calls powerCycle() after close()
+ * Checks status
+ */
+TEST_F(NfcHidlTest, PowerCycleAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::FAILED, nfc_->powerCycle());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+/*
* CoreInitialized:
- * Calls coreInitialized()
+ * Calls coreInitialized() with different data
* Waits for NfcEvent.POST_INIT_CPLT
*/
TEST_F(NfcHidlTest, CoreInitialized) {
NfcData data;
data.resize(1);
- data[0] = 0;
- EXPECT_EQ(NfcStatus::OK, nfc_->coreInitialized(data));
- // Wait for NfcEvent.POST_INIT_CPLT
- EXPECT_EQ(std::cv_status::no_timeout, wait());
- EXPECT_EQ(NfcEvent::POST_INIT_CPLT, last_event_);
+ for (int i = 0; i <= 6; i++)
+ {
+ data[0] = i;
+ EXPECT_EQ(NfcStatus::OK, nfc_->coreInitialized(data));
+ // Wait for NfcEvent.POST_INIT_CPLT
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::POST_INIT_CPLT, last_event_);
+ }
}
/*
@@ -348,6 +372,27 @@
EXPECT_EQ(NfcStatus::OK, nfc_->controlGranted());
}
+/*
+ * ControlGrantedAfterClose:
+ * Call controlGranted() after close
+ * Checks the return value
+ */
+TEST_F(NfcHidlTest, ControlGrantedAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->controlGranted());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
/* PreDiscover:
* Calls prediscover()
* Checks the return value
@@ -356,6 +401,59 @@
EXPECT_EQ(NfcStatus::OK, nfc_->prediscover());
}
+/*
+ * PreDiscoverAfterClose:
+ * Call prediscover() after close
+ * Checks the return value
+ */
+TEST_F(NfcHidlTest, PreDiscoverAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->prediscover());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+/*
+ * CloseAfterClose:
+ * Calls close() multiple times
+ * Checks status
+ */
+TEST_F(NfcHidlTest, CloseAfterClose) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->close());
+ // Wait for CLOSE_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::CLOSE_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+
+ EXPECT_EQ(NfcStatus::FAILED, nfc_->close());
+
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ // Wait for OPEN_CPLT event
+ EXPECT_EQ(std::cv_status::no_timeout, wait());
+ EXPECT_EQ(NfcEvent::OPEN_CPLT, last_event_);
+ EXPECT_EQ(NfcStatus::OK, last_status_);
+}
+
+
+/*
+ * OpenAfterOpen:
+ * Calls open() multiple times
+ * Checks status
+ */
+TEST_F(NfcHidlTest, OpenAfterOpen) {
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+ EXPECT_EQ(NfcStatus::OK, nfc_->open(nfc_cb_));
+}
+
int main(int argc, char** argv) {
::testing::AddGlobalTestEnvironment(new NfcHidlEnvironment);
::testing::InitGoogleTest(&argc, argv);