diff options
Diffstat (limited to 'automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp')
-rw-r--r-- | automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp | 491 |
1 files changed, 491 insertions, 0 deletions
diff --git a/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp new file mode 100644 index 0000000000..f81b1a2998 --- /dev/null +++ b/automotive/vehicle/aidl/impl/vhal/test/SubscriptionManagerTest.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2021 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 "SubscriptionManager.h" + +#include <VehicleHalTypes.h> + +#include <aidl/android/hardware/automotive/vehicle/BnVehicleCallback.h> +#include <android-base/thread_annotations.h> +#include <gmock/gmock.h> +#include <gtest/gtest.h> + +#include <float.h> +#include <chrono> +#include <list> +#include <memory> +#include <mutex> +#include <thread> +#include <vector> + +namespace android { +namespace hardware { +namespace automotive { +namespace vehicle { + +using ::aidl::android::hardware::automotive::vehicle::BnVehicleCallback; +using ::aidl::android::hardware::automotive::vehicle::GetValueResults; +using ::aidl::android::hardware::automotive::vehicle::IVehicleCallback; +using ::aidl::android::hardware::automotive::vehicle::SetValueResults; +using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropErrors; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue; +using ::aidl::android::hardware::automotive::vehicle::VehiclePropValues; +using ::ndk::ScopedAStatus; +using ::ndk::SpAIBinder; +using ::testing::ElementsAre; +using ::testing::WhenSorted; + +class PropertyCallback final : public BnVehicleCallback { + public: + ScopedAStatus onGetValues(const GetValueResults&) override { return ScopedAStatus::ok(); } + + ScopedAStatus onSetValues(const SetValueResults&) override { return ScopedAStatus::ok(); } + + ScopedAStatus onPropertyEvent(const VehiclePropValues& values, int32_t) override { + std::scoped_lock<std::mutex> lockGuard(mLock); + for (const auto& value : values.payloads) { + mEvents.push_back(value); + } + return ScopedAStatus::ok(); + } + + ScopedAStatus onPropertySetError(const VehiclePropErrors&) override { + return ScopedAStatus::ok(); + } + + // Test functions. + std::list<VehiclePropValue> getEvents() { + std::scoped_lock<std::mutex> lockGuard(mLock); + return mEvents; + } + + void clearEvents() { + std::scoped_lock<std::mutex> lockGuard(mLock); + mEvents.clear(); + } + + private: + std::mutex mLock; + std::list<VehiclePropValue> mEvents GUARDED_BY(mLock); +}; + +class SubscriptionManagerTest : public ::testing::Test { + public: + void SetUp() override { + mManager = std::make_unique<SubscriptionManager>( + [](const std::shared_ptr<IVehicleCallback>& callback, + const VehiclePropValue& value) { + callback->onPropertyEvent( + VehiclePropValues{ + .payloads = {value}, + }, + 0); + }); + mCallback = ::ndk::SharedRefBase::make<PropertyCallback>(); + // Keep the local binder alive. + mBinder = mCallback->asBinder(); + mCallbackClient = IVehicleCallback::fromBinder(mBinder); + } + + SubscriptionManager* getManager() { return mManager.get(); } + + std::shared_ptr<IVehicleCallback> getCallbackClient() { return mCallbackClient; } + + PropertyCallback* getCallback() { return mCallback.get(); } + + std::list<VehiclePropValue> getEvents() { return getCallback()->getEvents(); } + + void clearEvents() { return getCallback()->clearEvents(); } + + private: + std::unique_ptr<SubscriptionManager> mManager; + std::shared_ptr<PropertyCallback> mCallback; + std::shared_ptr<IVehicleCallback> mCallbackClient; + SpAIBinder mBinder; +}; + +TEST_F(SubscriptionManagerTest, testSubscribeGlobalContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 10.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_GE(getEvents().size(), static_cast<size_t>(9)); + EXPECT_EQ(getEvents().back().prop, 0); + EXPECT_EQ(getEvents().back().areaId, 0); +} + +TEST_F(SubscriptionManagerTest, testSubscribeMultiplePropsGlobalContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 10.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 20.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + size_t event0Count = 0; + size_t event1Count = 0; + + for (const auto& event : getEvents()) { + if (event.prop == 0) { + event0Count++; + } else { + event1Count++; + } + } + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(event0Count, static_cast<size_t>(9)); + // Theoretically trigger 20 times, but check for at least 15 times to be stable. + EXPECT_GE(event1Count, static_cast<size_t>(15)); +} + +TEST_F(SubscriptionManagerTest, testOverrideSubscriptionContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 20.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + // Override sample rate to be 10.0. + options[0].sampleRate = 10.0; + result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(getEvents().size(), static_cast<size_t>(9)); + EXPECT_LE(getEvents().size(), static_cast<size_t>(15)); +} + +TEST_F(SubscriptionManagerTest, testSubscribeMultipleAreasContinuous) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + size_t area0Count = 0; + size_t area1Count = 0; + + for (const auto& event : getEvents()) { + if (event.areaId == 0) { + area0Count++; + } else { + area1Count++; + } + } + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(area0Count, static_cast<size_t>(9)); + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(area1Count, static_cast<size_t>(9)); +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeGlobalContinuous) { + std::vector<SubscribeOptions> options = {{ + .propId = 0, + .areaIds = {0}, + .sampleRate = 10.0, + }}; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get()); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + clearEvents(); + + std::this_thread::sleep_for(std::chrono::milliseconds(200)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + ASSERT_TRUE(getEvents().empty()); +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeMultipleAreas) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + .sampleRate = 10.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0})); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + clearEvents(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + // Theoretically trigger 10 times, but check for at least 9 times to be stable. + EXPECT_GE(getEvents().size(), static_cast<size_t>(9)); + + for (const auto& event : getEvents()) { + EXPECT_EQ(event.prop, 1); + } +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeByCallback) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + .sampleRate = 10.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get()); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + clearEvents(); + + std::this_thread::sleep_for(std::chrono::seconds(1)); + + EXPECT_TRUE(getEvents().empty()); +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeFailure) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + }, + { + .propId = 1, + .areaIds = {0}, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + // Property ID: 2 was not subscribed. + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0, 1, 2})); + ASSERT_FALSE(result.ok()) << "unsubscribe an unsubscribed property must fail"; + + // Since property 0 and property 1 was not unsubscribed successfully, we should be able to + // unsubscribe them again. + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0, 1})); + ASSERT_TRUE(result.ok()) << "a failed unsubscription must not unsubscribe any properties" + << result.error().message(); +} + +TEST_F(SubscriptionManagerTest, testSubscribeOnchange) { + std::vector<SubscribeOptions> options1 = { + { + .propId = 0, + .areaIds = {0, 1}, + }, + { + .propId = 1, + .areaIds = {0}, + }, + }; + std::vector<SubscribeOptions> options2 = { + { + .propId = 0, + .areaIds = {0}, + }, + }; + + SpAIBinder binder1 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder(); + std::shared_ptr<IVehicleCallback> client1 = IVehicleCallback::fromBinder(binder1); + SpAIBinder binder2 = ::ndk::SharedRefBase::make<PropertyCallback>()->asBinder(); + std::shared_ptr<IVehicleCallback> client2 = IVehicleCallback::fromBinder(binder2); + auto result = getManager()->subscribe(client1, options1, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + result = getManager()->subscribe(client2, options2, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + std::vector<VehiclePropValue> updatedValues = { + { + .prop = 0, + .areaId = 0, + }, + { + .prop = 0, + .areaId = 1, + }, + { + .prop = 1, + .areaId = 0, + }, + { + .prop = 1, + .areaId = 1, + }, + }; + auto clients = getManager()->getSubscribedClients(updatedValues); + + ASSERT_THAT(clients[client1], + WhenSorted(ElementsAre(&updatedValues[0], &updatedValues[1], &updatedValues[2]))); + ASSERT_THAT(clients[client2], ElementsAre(&updatedValues[0])); +} + +TEST_F(SubscriptionManagerTest, testSubscribeInvalidOption) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1, 2, 3, 4}, + // invalid sample rate. + .sampleRate = 0.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_FALSE(result.ok()) << "subscribe with invalid sample rate must fail"; + ASSERT_TRUE(getManager() + ->getSubscribedClients({{ + .prop = 0, + .areaId = 0, + }, + { + .prop = 1, + .areaId = 0, + }}) + .empty()) + << "no property should be subscribed if error is returned"; +} + +TEST_F(SubscriptionManagerTest, testSubscribeNoAreaIds) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {}, + .sampleRate = 1.0, + }, + { + .propId = 1, + .areaIds = {0}, + .sampleRate = 10.0, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, true); + ASSERT_FALSE(result.ok()) << "subscribe with invalid sample rate must fail"; + ASSERT_TRUE(getManager() + ->getSubscribedClients({{ + .prop = 1, + .areaId = 0, + }}) + .empty()) + << "no property should be subscribed if error is returned"; +} + +TEST_F(SubscriptionManagerTest, testUnsubscribeOnchange) { + std::vector<SubscribeOptions> options = { + { + .propId = 0, + .areaIds = {0, 1}, + }, + { + .propId = 1, + .areaIds = {0}, + }, + }; + + auto result = getManager()->subscribe(getCallbackClient(), options, false); + ASSERT_TRUE(result.ok()) << "failed to subscribe: " << result.error().message(); + + result = getManager()->unsubscribe(getCallbackClient()->asBinder().get(), + std::vector<int32_t>({0})); + ASSERT_TRUE(result.ok()) << "failed to unsubscribe: " << result.error().message(); + + std::vector<VehiclePropValue> updatedValues = { + { + .prop = 0, + .areaId = 0, + }, + { + .prop = 1, + .areaId = 0, + }, + }; + auto clients = getManager()->getSubscribedClients(updatedValues); + + ASSERT_THAT(clients[getCallbackClient()], ElementsAre(&updatedValues[1])); +} + +TEST_F(SubscriptionManagerTest, testCheckSampleRateValid) { + ASSERT_TRUE(SubscriptionManager::checkSampleRate(1.0)); +} + +TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidTooSmall) { + ASSERT_FALSE(SubscriptionManager::checkSampleRate(FLT_MIN)); +} + +TEST_F(SubscriptionManagerTest, testCheckSampleRateInvalidZero) { + ASSERT_FALSE(SubscriptionManager::checkSampleRate(0)); +} + +} // namespace vehicle +} // namespace automotive +} // namespace hardware +} // namespace android |