diff --git a/automotive/vehicle/2.0/vts/functional/Android.bp b/automotive/vehicle/2.0/vts/functional/Android.bp new file mode 100644 index 0000000000..9f1dd6fb86 --- /dev/null +++ b/automotive/vehicle/2.0/vts/functional/Android.bp @@ -0,0 +1,21 @@ +cc_test { + name: "VtsHalAutomotiveVehicleV2_0TargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + ], + srcs: [ + "VtsHalAutomotiveVehicleV2_0TargetTest.cpp", + ], + shared_libs: [ + "libbase", + "libhidlbase", + "liblog", + ], + static_libs: [ + "android.hardware.automotive.vehicle@2.0", + ], + test_suites: [ + "vts", + "general-tests", + ], +} diff --git a/automotive/vehicle/2.0/vts/functional/VtsHalAutomotiveVehicleV2_0TargetTest.cpp b/automotive/vehicle/2.0/vts/functional/VtsHalAutomotiveVehicleV2_0TargetTest.cpp new file mode 100644 index 0000000000..7f1d4d10b6 --- /dev/null +++ b/automotive/vehicle/2.0/vts/functional/VtsHalAutomotiveVehicleV2_0TargetTest.cpp @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2020 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 "VtsHalAutomotiveVehicle" + +#include +#include + +#include +#include +#include + +using namespace android::hardware::automotive::vehicle::V2_0; +using ::android::sp; +using ::android::hardware::hidl_vec; +using ::android::hardware::Return; + +constexpr auto kTimeout = std::chrono::milliseconds(500); +constexpr auto kInvalidProp = 0x31600207; + +class VtsVehicleCallback : public IVehicleCallback { + private: + using MutexGuard = std::lock_guard; + using HidlVecOfValues = hidl_vec; + std::mutex mLock; + std::condition_variable mEventCond; + std::vector mReceivedEvents; + + public: + Return onPropertyEvent(const hidl_vec& values) override { + { + MutexGuard guard(mLock); + mReceivedEvents.push_back(values); + } + mEventCond.notify_one(); + return Return(); + } + + Return onPropertySet(const VehiclePropValue& /* value */) override { + return Return(); + } + Return onPropertySetError(StatusCode /* errorCode */, int32_t /* propId */, + int32_t /* areaId */) override { + return Return(); + } + + bool waitForExpectedEvents(size_t expectedEvents) { + std::unique_lock g(mLock); + + if (expectedEvents == 0 && mReceivedEvents.size() == 0) { + return mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout; + } + + while (expectedEvents != mReceivedEvents.size()) { + if (mEventCond.wait_for(g, kTimeout) == std::cv_status::timeout) { + return false; + } + } + return true; + } + + void reset() { mReceivedEvents.clear(); } +}; + +class VehicleHalHidlTest : public testing::TestWithParam { + public: + virtual void SetUp() override { + mVehicle = IVehicle::getService(GetParam()); + ASSERT_NE(mVehicle.get(), nullptr); + } + virtual void TearDown() override {} + + sp mVehicle; + + bool isBooleanGlobalProp(int32_t property) { + return (property & (int)VehiclePropertyType::MASK) == (int)VehiclePropertyType::BOOLEAN && + (property & (int)VehicleArea::MASK) == (int)VehicleArea::GLOBAL; + } + + void invokeGet(int32_t property, int32_t areaId) { + VehiclePropValue requestedValue{}; + requestedValue.prop = property; + requestedValue.areaId = areaId; + + invokeGet(requestedValue); + } + + void invokeGet(const VehiclePropValue& requestedPropValue) { + mActualValue = VehiclePropValue{}; // reset previous values + + StatusCode refStatus; + VehiclePropValue refValue; + bool isCalled = false; + mVehicle->get(requestedPropValue, + [&refStatus, &refValue, &isCalled](StatusCode status, + const VehiclePropValue& value) { + refStatus = status; + refValue = value; + isCalled = true; + }); + ASSERT_TRUE(isCalled) << "callback wasn't called for property: " << requestedPropValue.prop; + + mActualValue = refValue; + mActualStatusCode = refStatus; + } + + VehiclePropValue mActualValue; + StatusCode mActualStatusCode; +}; + +// Test getAllPropConfig() returns at least 4 property configs. +TEST_P(VehicleHalHidlTest, getAllPropConfigs) { + ALOGD("VehicleHalHidlTest::getAllPropConfigs"); + bool isCalled = false; + hidl_vec propConfigs; + mVehicle->getAllPropConfigs([&isCalled, &propConfigs](const hidl_vec& cfgs) { + propConfigs = cfgs; + isCalled = true; + }); + ASSERT_TRUE(isCalled); + ASSERT_GE(propConfigs.size(), 4); +} + +// Test getPropConfig() can query all properties listed in CDD. +TEST_P(VehicleHalHidlTest, getPropConfigs) { + ALOGD("VehicleHalHidlTest::getPropConfigs"); + // Check the properties listed in CDD + hidl_vec properties = { + (int)VehicleProperty::GEAR_SELECTION, (int)VehicleProperty::NIGHT_MODE, + (int)VehicleProperty::PARKING_BRAKE_ON, (int)VehicleProperty::PERF_VEHICLE_SPEED}; + bool isCalled = false; + mVehicle->getPropConfigs( + properties, [&isCalled](StatusCode status, const hidl_vec& cfgs) { + ASSERT_EQ(StatusCode::OK, status); + ASSERT_EQ(4u, cfgs.size()); + isCalled = true; + }); + ASSERT_TRUE(isCalled); +} + +// Test getPropConfig() with an invalid propertyId returns an error code. +TEST_P(VehicleHalHidlTest, getPropConfigsWithInvalidProp) { + ALOGD("VehicleHalHidlTest::getPropConfigsWithInvalidProp"); + hidl_vec properties = {kInvalidProp}; + bool isCalled = false; + mVehicle->getPropConfigs( + properties, [&isCalled](StatusCode status, const hidl_vec& cfgs) { + ASSERT_NE(StatusCode::OK, status); + ASSERT_EQ(0, cfgs.size()); + isCalled = true; + }); + ASSERT_TRUE(isCalled); +} + +// Test get() return current value for properties. +TEST_P(VehicleHalHidlTest, get) { + ALOGD("VehicleHalHidlTest::get"); + invokeGet((int)VehicleProperty::PERF_VEHICLE_SPEED, 0); + ASSERT_EQ(StatusCode::OK, mActualStatusCode); +} + +// Test get() with an invalid propertyId return an error codes. +TEST_P(VehicleHalHidlTest, getInvalidProp) { + ALOGD("VehicleHalHidlTest::getInvalidProp"); + + invokeGet(kInvalidProp, 0); + ASSERT_NE(StatusCode::OK, mActualStatusCode); +} + +// Test set() on read_write properties. +TEST_P(VehicleHalHidlTest, setProp) { + ALOGD("VehicleHalHidlTest::setProp"); + hidl_vec propConfigs; + mVehicle->getAllPropConfigs( + [&propConfigs](const hidl_vec& cfgs) { propConfigs = cfgs; }); + for (const VehiclePropConfig& cfg : propConfigs) { + // test on boolean and writable property + if (cfg.access == VehiclePropertyAccess::READ_WRITE && isBooleanGlobalProp(cfg.prop)) { + invokeGet(cfg.prop, 0); + int setValue = mActualValue.value.int32Values[0] == 1 ? 0 : 1; + VehiclePropValue propToSet = mActualValue; + propToSet.value.int32Values[0] = setValue; + ASSERT_EQ(StatusCode::OK, mVehicle->set(propToSet)); + // check set success + invokeGet(cfg.prop, 0); + ASSERT_EQ(StatusCode::OK, mActualStatusCode); + ASSERT_EQ(setValue, mActualValue.value.int32Values[0]); + } + } +} + +// Test set() on an read_only property. +TEST_P(VehicleHalHidlTest, setNotWritableProp) { + ALOGD("VehicleHalHidlTest::setNotWritableProp"); + invokeGet(static_cast(VehicleProperty::PERF_VEHICLE_SPEED), 0); + ASSERT_EQ(StatusCode::OK, mActualStatusCode); + VehiclePropValue vehicleSpeed = mActualValue; + + ASSERT_EQ(StatusCode::ACCESS_DENIED, mVehicle->set(vehicleSpeed)); +} + +// Test subscribe() and unsubscribe(). +TEST_P(VehicleHalHidlTest, subscribeAndUnsubscribe) { + ALOGD("VehicleHalHidlTest::subscribeAndUnsubscribe"); + const auto prop = static_cast(VehicleProperty::PERF_VEHICLE_SPEED); + sp cb = new VtsVehicleCallback(); + + hidl_vec options = { + SubscribeOptions{.propId = prop, 100.0, .flags = SubscribeFlags::EVENTS_FROM_CAR}}; + + ASSERT_EQ(StatusCode::OK, mVehicle->subscribe(cb, options)); + ASSERT_TRUE(cb->waitForExpectedEvents(10)); + + ASSERT_EQ(StatusCode::OK, mVehicle->unsubscribe(cb, prop)); + cb->reset(); + ASSERT_FALSE(cb->waitForExpectedEvents(10)); +} + +// Test subscribe() with an invalid property. +TEST_P(VehicleHalHidlTest, subscribeInvalidProp) { + ALOGD("VehicleHalHidlTest::subscribeInvalidProp"); + + sp cb = new VtsVehicleCallback(); + + hidl_vec options = {SubscribeOptions{ + .propId = kInvalidProp, 10.0, .flags = SubscribeFlags::EVENTS_FROM_CAR}}; + + ASSERT_NE(StatusCode::OK, mVehicle->subscribe(cb, options)); +} + +INSTANTIATE_TEST_SUITE_P( + PerInstance, VehicleHalHidlTest, + testing::ValuesIn(android::hardware::getAllHalInstanceNames(IVehicle::descriptor)), + android::hardware::PrintInstanceNameToString);