496 lines
17 KiB
C++
496 lines
17 KiB
C++
/*
|
|
* 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 <MockVehicleHardware.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 {
|
|
mHardware = std::make_shared<MockVehicleHardware>();
|
|
mManager = std::make_unique<SubscriptionManager>(mHardware.get());
|
|
mCallback = ndk::SharedRefBase::make<PropertyCallback>();
|
|
// Keep the local binder alive.
|
|
mBinder = mCallback->asBinder();
|
|
mCallbackClient = IVehicleCallback::fromBinder(mBinder);
|
|
std::shared_ptr<IVehicleCallback> callbackClient = mCallbackClient;
|
|
mHardware->registerOnPropertyChangeEvent(
|
|
std::make_unique<IVehicleHardware::PropertyChangeCallback>(
|
|
[callbackClient](std::vector<VehiclePropValue> updatedValues) {
|
|
VehiclePropValues values = {
|
|
.payloads = std::move(updatedValues),
|
|
};
|
|
callbackClient->onPropertyEvent(values, 0);
|
|
}));
|
|
}
|
|
|
|
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;
|
|
std::shared_ptr<MockVehicleHardware> mHardware;
|
|
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
|