/*
* Copyright (C) 2016 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 <functional>
#include <iostream>
#include <unordered_map>
#include <gtest/gtest.h>
#include "vhal_v2_0/SubscriptionManager.h"
#include "VehicleHalTestUtils.h"
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace {
using namespace std::placeholders;
class SubscriptionManagerTest : public ::testing::Test {
public:
SubscriptionManagerTest() : manager(([this](int x) { onPropertyUnsubscribed(x); })) {}
SubscriptionManager manager;
static constexpr int32_t PROP1 = toInt(VehicleProperty::HVAC_FAN_SPEED);
static constexpr int32_t PROP2 = toInt(VehicleProperty::DISPLAY_BRIGHTNESS);
sp<IVehicleCallback> cb1 = new MockedVehicleCallback();
sp<IVehicleCallback> cb2 = new MockedVehicleCallback();
sp<IVehicleCallback> cb3 = new MockedVehicleCallback();
void SetUp() override {
lastUnsubscribedProperty = -1;
}
hidl_vec<SubscribeOptions> subscrToProp1 = {
SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::EVENTS_FROM_CAR},
};
hidl_vec<SubscribeOptions> subscrToProp2 = {
SubscribeOptions{.propId = PROP2, .flags = SubscribeFlags::EVENTS_FROM_CAR},
};
hidl_vec<SubscribeOptions> subscrToProp1and2 = {
SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::EVENTS_FROM_CAR},
SubscribeOptions{.propId = PROP2, .flags = SubscribeFlags::EVENTS_FROM_CAR},
};
static std::list<sp<IVehicleCallback>> extractCallbacks(
const std::list<sp<HalClient>>& clients) {
std::list<sp<IVehicleCallback>> callbacks;
for (auto c : clients) {
callbacks.push_back(c->getCallback());
}
return callbacks;
}
std::list<sp<HalClient>> clientsToProp1() {
return manager.getSubscribedClients(PROP1, SubscribeFlags::EVENTS_FROM_CAR);
}
std::list<sp<HalClient>> clientsToProp2() {
return manager.getSubscribedClients(PROP2, SubscribeFlags::EVENTS_FROM_CAR);
}
void onPropertyUnsubscribed(int propertyId) {
// Called when there are no clients who subscribed to particular property. This can happen
// because of explict unsubscribe call or when client (IVehicleCallback) was disconnected.
lastUnsubscribedProperty = propertyId;
}
void assertOnPropertyUnsubscribedNotCalled() {
ASSERT_EQ(-1, lastUnsubscribedProperty);
}
void assertLastUnsubscribedProperty(int expectedPropertyId) {
ASSERT_EQ(expectedPropertyId, lastUnsubscribedProperty);
lastUnsubscribedProperty = -1;
}
private:
int lastUnsubscribedProperty;
};
TEST_F(SubscriptionManagerTest, multipleClients) {
std::list<SubscribeOptions> updatedOptions;
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(1, cb1, subscrToProp1, &updatedOptions));
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(2, cb2, subscrToProp1, &updatedOptions));
auto clients = manager.getSubscribedClients(PROP1, SubscribeFlags::EVENTS_FROM_CAR);
ASSERT_ALL_EXISTS({cb1, cb2}, extractCallbacks(clients));
}
TEST_F(SubscriptionManagerTest, negativeCases) {
std::list<SubscribeOptions> updatedOptions;
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(1, cb1, subscrToProp1, &updatedOptions));
// Wrong prop
auto clients = manager.getSubscribedClients(toInt(VehicleProperty::AP_POWER_BOOTUP_REASON),
SubscribeFlags::EVENTS_FROM_CAR);
ASSERT_TRUE(clients.empty());
// Wrong flag
clients = manager.getSubscribedClients(PROP1, SubscribeFlags::EVENTS_FROM_ANDROID);
ASSERT_TRUE(clients.empty());
}
TEST_F(SubscriptionManagerTest, mulipleSubscriptions) {
std::list<SubscribeOptions> updatedOptions;
ASSERT_EQ(StatusCode::OK, manager.addOrUpdateSubscription(1, cb1, subscrToProp1,
&updatedOptions));
auto clients = manager.getSubscribedClients(PROP1, SubscribeFlags::EVENTS_FROM_CAR);
ASSERT_EQ((size_t) 1, clients.size());
ASSERT_EQ(cb1, clients.front()->getCallback());
// Same property, but different zone, to make sure we didn't unsubscribe
// from previous zone.
ASSERT_EQ(
StatusCode::OK,
manager.addOrUpdateSubscription(
1, cb1, {SubscribeOptions{.propId = PROP1, .flags = SubscribeFlags::EVENTS_FROM_CAR}},
&updatedOptions));
clients = manager.getSubscribedClients(PROP1, SubscribeFlags::EVENTS_FROM_CAR);
ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
clients = manager.getSubscribedClients(PROP1, SubscribeFlags::EVENTS_FROM_CAR);
ASSERT_ALL_EXISTS({cb1}, extractCallbacks(clients));
}
TEST_F(SubscriptionManagerTest, unsubscribe) {
std::list<SubscribeOptions> updatedOptions;
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(1, cb1, subscrToProp1, &updatedOptions));
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(2, cb2, subscrToProp2, &updatedOptions));
ASSERT_EQ(StatusCode::OK,
manager.addOrUpdateSubscription(3, cb3, subscrToProp1and2, &updatedOptions));
ASSERT_ALL_EXISTS({ cb1, cb3 }, extractCallbacks(clientsToProp1()));
ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
manager.unsubscribe(1, PROP1);
assertOnPropertyUnsubscribedNotCalled();
ASSERT_ALL_EXISTS({cb3}, extractCallbacks(clientsToProp1()));
// Make sure nothing changed in PROP2 so far.
ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
// No one subscribed to PROP1, subscription for PROP2 is not affected.
manager.unsubscribe(3, PROP1);
assertLastUnsubscribedProperty(PROP1);
ASSERT_ALL_EXISTS({cb2, cb3}, extractCallbacks(clientsToProp2()));
manager.unsubscribe(3, PROP2);
assertOnPropertyUnsubscribedNotCalled();
ASSERT_ALL_EXISTS({cb2}, extractCallbacks(clientsToProp2()));
// The last client unsubscribed from this property.
manager.unsubscribe(2, PROP2);
assertLastUnsubscribedProperty(PROP2);
// No one subscribed anymore
manager.unsubscribe(1, PROP1);
assertLastUnsubscribedProperty(PROP1);
}
} // namespace anonymous
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android