Files
cariflex/tools/EVerest-main/modules/EVSE/Auth/tests/reservation_tests.cpp
Eric F d398a6ced2 Add extracted tools: CitrineOS, OpenOCPP, ShapeShifter
- CitrineOS core extracted (CSMS OCPP 2.0.1)
- OpenOCPP extracted (firmware OCPP 1.6J/2.0.1)
- ShapeShifter library installed (pip install -e)
- ShapeShifter specification extracted
- EVerest extracted

TODO updated with progress
2026-06-08 00:38:27 -04:00

1456 lines
84 KiB
C++

// SPDX-License-Identifier: Apache-2.0
// Copyright Pionix GmbH and Contributors to EVerest
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/date.hpp>
#include <generated/interfaces/kvs/Interface.hpp>
#define private public
// Make 'ReservationHandler.hpp privates public to test a helper function 'get_all_possible_orders'.
#include "ReservationHandler.hpp"
#undef private
using testing::_;
using testing::MockFunction;
using testing::Return;
using testing::SaveArg;
using namespace types::reservation;
namespace module {
class ReservationHandlerTest : public ::testing::Test {
private:
uint32_t reservation_id = 0;
protected:
Reservation create_reservation(const types::evse_manager::ConnectorTypeEnum connector_type) {
return Reservation{static_cast<int32_t>(reservation_id),
"TOKEN_" + std::to_string(reservation_id++),
Everest::Date::to_rfc3339((date::utc_clock::now()) + std::chrono::hours(1)),
std::nullopt,
std::nullopt,
connector_type};
}
void add_connector(const int32_t evse_id, const uint32_t connector_id,
const types::evse_manager::ConnectorTypeEnum type,
std::map<int32_t, std::unique_ptr<EVSEContext>>& evses) {
if (evses.count(evse_id) > 0) {
evses[evse_id]->connectors.push_back(Connector{static_cast<int>(connector_id), type});
} else {
evses[evse_id] = std::make_unique<EVSEContext>(evse_id, evse_id - 1, connector_id, type);
}
}
kvsIntf kvs;
std::map<int32_t, std::unique_ptr<EVSEContext>> evses;
ReservationHandler r{evses, "reservation_kvs", &kvs};
};
TEST_F(ReservationHandlerTest, global_reservation_scenario_01) {
// Test global reservations (not bound to specific evse id). 3 EVSE's, one with cCCS2, two with cCCS2 and cType2.
// Three cCCS2 reservations should be accepted.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_02) {
// Test global reservations (not bound to specific evse id). 3 EVSE's, one with cCCS2, two with cCCS2 and cType2.
// One cCCS2 and one cType2 reservation should be accepted, but another reservation can not be made. Because if
// there would be two cCCS2 and one cType2, it is possible that first two cCCS2 type cars arrive, charge at the
// two combined charging stations and when the cType2 car arrives, there is no charging station with this connector
// available anymore.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_03) {
// Test global reservations (not bound to specific evse id). 3 EVSE's, one with cCCS2, two with cCCS2 and cType2.
// Two cType2 reservations should be accepted, but a third reservation is not accepted, because it is not guaranteed
// that in all circumstances a charger is available (for example cCCS2 goes to evse 2, cType2 goes to connector
// 1 and the second cType2 arrives but no charger is available anymore).
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_04) {
// Test global reservations (not bound to specific evse id). 3 EVSE's, one with cCCS2, two with cCCS2 and cType2.
// A cCCS2 and cType2 reservation should be accepted, because it does not matter in which order they arrive, there
// is always an evse available for the other one. But a cType2 as third reservation is not possible. Imagine the
// first car that arrives is cCCS2 and charges at evse 4 or 7, the second car can only put it at 4 or 7, then
// the third car that arrives (cType2) does not have an EVSE for his type available.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(4, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(4, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(7, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(7, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_05) {
// Test global reservations (not bound to specific evse id). 4 EVSE's, three with cCCS2, one with cCCS2 and cType2.
// When a cCCS2 reservation is made, cType2 can not make a reservation anymore, because it is possible that when
// the cCCS2 car first arrives, there is no EVSE available for the cType2 car anymore (evse 2).
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(3, 5, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_06) {
// Test global reservations (not bound to specific evse id). 3 EVSE's, two with cCCS2, one with cCCS2 and cType2.
// Only one cType2 reservation can be made and nothing else, also no cCCS2 reservation (because when the cCCS2 car
// arives first and puts it on connector 2, the cType2 that arrives second does not have an EVSE available anymore).
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_07) {
// Test global reservations (not bound to specific evse id). 1 EVSE only. Unknown is accepted, a type that is not
// available is rejected.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::sType3, evses);
// There is no cType2 connector on this evse.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Rejected);
// Unknown is accepted
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Accepted);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_08) {
// Test global reservations (not bound to specific evse id). 3 EVSE's with all cCCS2 and cType2 connectors.
// Unknown and cCCS2 reservations are accepted, max 3 in total.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_09) {
// Test global reservations (not bound to specific evse id). 3 EVSE's with all cCCS2 and cType2 connectors.
// Unknown, cType2 and cCCS2 reservations are accepted, max 3 in total.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_10) {
// Test global reservations (not bound to specific evse id). 3 EVSE's with all two 'Unknown' connectors.
// Three reservations are accepted in total, it does not matter what connector types they have.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::Unknown, evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::Unknown, evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::Unknown, evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::Unknown, evses);
add_connector(5, 0, types::evse_manager::ConnectorTypeEnum::Unknown, evses);
add_connector(5, 1, types::evse_manager::ConnectorTypeEnum::Unknown, evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Other3Ph)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_11) {
// Test global reservations (not bound to specific evse id). 3 EVSE's with only one cCCS2 connector each.
// In total three reservations are accepted with the correct type (cCCS2 or Unknown).
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::Unknown)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Rejected);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_scenario_12) {
// Test global reservations (not bound to specific evse id). One EVSE with cCCS2 and cType2, one with cType2 and
// cTesla, one with cTesla and cCCS2.
// Only two reservations can be accepted, for the third there is no guarantee there is always place to charge in all
// orders of arrival of the different cars.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cTesla, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cTesla, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType1)),
ReservationResult::Rejected);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cTesla)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, get_all_possible_orders) {
using namespace types::evse_manager;
std::vector<ConnectorTypeEnum> connectors;
connectors.push_back(ConnectorTypeEnum::cCCS2);
std::vector<std::vector<ConnectorTypeEnum>> result = r.get_all_possible_orders(connectors);
EXPECT_EQ(result, std::vector<std::vector<ConnectorTypeEnum>>({{ConnectorTypeEnum::cCCS2}}));
connectors.push_back(ConnectorTypeEnum::cCCS2);
result = r.get_all_possible_orders(connectors);
EXPECT_EQ(result,
std::vector<std::vector<ConnectorTypeEnum>>({{ConnectorTypeEnum::cCCS2, ConnectorTypeEnum::cCCS2}}));
connectors.push_back(ConnectorTypeEnum::cCCS1);
result = r.get_all_possible_orders(connectors);
EXPECT_EQ(result, std::vector<std::vector<ConnectorTypeEnum>>(
{{ConnectorTypeEnum::cCCS2, ConnectorTypeEnum::cCCS2, ConnectorTypeEnum::cCCS1},
{ConnectorTypeEnum::cCCS2, ConnectorTypeEnum::cCCS1, ConnectorTypeEnum::cCCS2},
{ConnectorTypeEnum::cCCS1, ConnectorTypeEnum::cCCS2, ConnectorTypeEnum::cCCS2}}));
}
TEST_F(ReservationHandlerTest, get_all_possible_orders2) {
using namespace types::evse_manager;
std::vector<ConnectorTypeEnum> connectors;
connectors.push_back(ConnectorTypeEnum::cType1);
std::vector<std::vector<ConnectorTypeEnum>> result = r.get_all_possible_orders(connectors);
EXPECT_EQ(result, std::vector<std::vector<ConnectorTypeEnum>>({{ConnectorTypeEnum::cType1}}));
connectors.push_back(ConnectorTypeEnum::Pan);
result = r.get_all_possible_orders(connectors);
EXPECT_EQ(result,
std::vector<std::vector<ConnectorTypeEnum>>({{ConnectorTypeEnum::cType1, ConnectorTypeEnum::Pan},
{ConnectorTypeEnum::Pan, ConnectorTypeEnum::cType1}}));
connectors.push_back(ConnectorTypeEnum::cCCS1);
result = r.get_all_possible_orders(connectors);
EXPECT_EQ(result, std::vector<std::vector<ConnectorTypeEnum>>(
{{ConnectorTypeEnum::cType1, ConnectorTypeEnum::Pan, ConnectorTypeEnum::cCCS1},
{ConnectorTypeEnum::Pan, ConnectorTypeEnum::cCCS1, ConnectorTypeEnum::cType1},
{ConnectorTypeEnum::Pan, ConnectorTypeEnum::cType1, ConnectorTypeEnum::cCCS1},
{ConnectorTypeEnum::cType1, ConnectorTypeEnum::cCCS1, ConnectorTypeEnum::Pan},
{ConnectorTypeEnum::cCCS1, ConnectorTypeEnum::Pan, ConnectorTypeEnum::cType1},
{ConnectorTypeEnum::cCCS1, ConnectorTypeEnum::cType1, ConnectorTypeEnum::Pan}}));
}
TEST_F(ReservationHandlerTest, specific_evse_scenario_01) {
// Test reservations for a specific evse.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// On EVSE1, there is no cCCS1 type connector.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS1)),
ReservationResult::Rejected);
// But there is a cCCS2 type connector, accept reservation.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// Another reservation on cCCS1 type connector will return Occupied, as there already is a reservation.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// But on another connector, the reservation can be made.
EXPECT_EQ(r.make_reservation(0, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// But only when there is not already a reservation for that specific connector.
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, specific_evse_scenario_02) {
// Test reservations for a specific evse.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// No cCCS2 type on evse 1 (only on 0, but that one is not reserved here).
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Rejected);
// But it has a cType2 connector.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// There already is a reservation for this EVSE, so 'Occupied' is returned.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// But on the other EVSE, the reservation can be made.
EXPECT_EQ(r.make_reservation(0, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// And now it is already reserved, so a second can not be made.
EXPECT_EQ(r.make_reservation(0, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_specific_evse_combination_scenario_01) {
// Test global reservation (not bound to specific EVSE) combined with reservation for a specific EVSE.
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
// Global reservation for cType2, this can be EVSE 0 or 1.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// Specific reservation for EVSE 1, the global reservation can still charge on EVSE 0.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// There already is a reservation for EVSE 1.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// Specific reservation for EVSE 2, the global reservation can still charge on EVSE 0.
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// Specific reservation for EVSE 0, but if this would be accepted, the global reservation can not charge anymore, so
// this is denied.
EXPECT_EQ(r.make_reservation(0, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// EVSE 1 is already occupied with a reservation.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// Global reservation, can not be made because then the first reservation can not charge anymore.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// Same for a cCCS2 global reservation.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_specific_evse_combination_scenario_02) {
// Test global reservation (not bound to specific EVSE) combined with reservation for a specific EVSE.
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
// Global reservation for cType2, this can charge on EVSE 0 or 1.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// Specific reservation for EVSE 1, the global reservation still has an EVSE left to charge on.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// EVSE 1 is already reserved.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// Another global reservation. This can not be made, because the first global reservation would not have been able
// to charge in that case.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// But a global reservation for cCCS2 is possible. Because as EVSE 1 is reserved, there is only one option left for
// this reservation, which is EVSE 2, and the first reservation can then still charge on EVSE 0.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// But another global reservation is not possible anymore.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// As well as any other specific reservation.
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(0, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, global_reservation_specific_evse_combination_scenario_03) {
// Test global reservation (not bound to specific EVSE) combined with reservation for a specific EVSE.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// Make a reservation for EVSE 2.
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// EVSE 2 already has a reservation.
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// Make a reservation for EVSE 1.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// A global reservation is possible, because EVSE 0 is still not reserved and has cCCS2.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// But another global reservation is not possible, because there are not enough cCCS2 connectors.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// And cType2 is also not possible, because it can arrive before the first global reservation and then put the car
// at EVSE 0, and then there will be no place for the car that did the first global reservation.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// But a specific reservation for EVSE 3 is possible, because the first global reservation can then still charge
// at EVSE 0.
EXPECT_EQ(r.make_reservation(3, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
}
TEST_F(ReservationHandlerTest, check_charging_possible_global_specific_reservations_scenario_01) {
// Do some specific reservations and check if charging is possible when a car arrives.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// Charging is possible on all EVSE's (except for the not existing one of course).
EXPECT_TRUE(r.is_charging_possible(0));
EXPECT_TRUE(r.is_charging_possible(2));
EXPECT_TRUE(r.is_charging_possible(1));
EXPECT_TRUE(r.is_charging_possible(3));
EXPECT_FALSE(r.is_charging_possible(4));
// But after a reservation on EVSE 2, charging is not possible on that EVSE anymore.
EXPECT_EQ(r.make_reservation(2, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_TRUE(r.is_charging_possible(0));
EXPECT_FALSE(r.is_charging_possible(2));
EXPECT_TRUE(r.is_charging_possible(1));
EXPECT_TRUE(r.is_charging_possible(3));
// Now EVSE 1 is also occupied, charging will also not be possible there.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_TRUE(r.is_charging_possible(0));
EXPECT_FALSE(r.is_charging_possible(2));
EXPECT_FALSE(r.is_charging_possible(1));
EXPECT_TRUE(r.is_charging_possible(3));
// A global reservation have been made for a cCCS2 charger. The only still available is the one on EVSE 0. That one
// must be available for the reservation at all times.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
// So that makes charging on EVSE 0 not possible.
EXPECT_FALSE(r.is_charging_possible(0));
// But the car can charge at EVSE 3 (as EVSE 0 is then still available for the global reservation).
EXPECT_TRUE(r.is_charging_possible(3));
// And now all reservations are made, no new car can make a reservation or charge anymore.
EXPECT_EQ(r.make_reservation(3, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_FALSE(r.is_charging_possible(0));
EXPECT_FALSE(r.is_charging_possible(1));
EXPECT_FALSE(r.is_charging_possible(2));
EXPECT_FALSE(r.is_charging_possible(3));
}
TEST_F(ReservationHandlerTest, check_charging_possible_global_specific_reservations_scenario_02) {
// Do some specific reservations and check if charging is possible when a car arrives.
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
// Charging is possible on all EVSE's.
EXPECT_TRUE(r.is_charging_possible(0));
EXPECT_TRUE(r.is_charging_possible(1));
EXPECT_TRUE(r.is_charging_possible(2));
// After a global reservation for cType2, charging is still possible on all EVSE's, is there are two cType2
// connectors, so when one car charges, there is still a cType2 available.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_TRUE(r.is_charging_possible(0));
EXPECT_TRUE(r.is_charging_possible(1));
EXPECT_TRUE(r.is_charging_possible(2));
// A reservation for EVSE 1 is made. Now the global reservation only has the possibility to charge on EVSE 0.
// So on that EVSE, charging is not possible anymore. And of course also not on EVSE 1 as that one is reserved.
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_FALSE(r.is_charging_possible(0));
EXPECT_FALSE(r.is_charging_possible(1));
EXPECT_TRUE(r.is_charging_possible(2));
// Another global reservation makes charging impossible on all EVSE's.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_FALSE(r.is_charging_possible(0));
EXPECT_FALSE(r.is_charging_possible(1));
EXPECT_FALSE(r.is_charging_possible(2));
}
TEST_F(ReservationHandlerTest, is_evse_reserved) {
// Check if a specific EVSE is reserved.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_FALSE(r.is_evse_reserved(0));
EXPECT_FALSE(r.is_evse_reserved(1));
// After a global reservation, no specific EVSE is still reserved.
r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2));
EXPECT_FALSE(r.is_evse_reserved(0));
EXPECT_FALSE(r.is_evse_reserved(1));
// But after a specific reservation, the EVSE of that reservation is reserved.
r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2));
EXPECT_FALSE(r.is_evse_reserved(0));
EXPECT_TRUE(r.is_evse_reserved(1));
}
TEST_F(ReservationHandlerTest, change_availability_scenario_01) {
// Change availability of an EVSE and check if reservations are cancelled.
std::optional<uint32_t> evse_id;
MockFunction<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const ReservationEndReason reason, const bool send_reservation_update)>
reservation_callback_mock;
r.register_reservation_cancelled_callback(reservation_callback_mock.AsStdFunction());
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// Four global reservations are made.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// Set an evse to not available, this will call the cancel reservation callback for the last reserved reservation
// id
EXPECT_CALL(reservation_callback_mock, Call(_, 3, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
EXPECT_FALSE(evse_id.has_value());
// Setting an evse to faulted will cancel the next reservation.
EXPECT_CALL(reservation_callback_mock, Call(_, 2, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[3]->connectors.at(0).get_state(), 3, 1);
EXPECT_FALSE(evse_id.has_value());
// Set evse to available again. This will not call a cancelled callback. And setting one to unavailable will also
// not cause the cancelled callback to be called because there is still one evse available.
EXPECT_CALL(reservation_callback_mock, Call(_, 2, ReservationEndReason::Cancelled, true)).Times(0);
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::ERROR_CLEARED);
r.on_connector_state_changed(this->evses[3]->connectors.at(0).get_state(), 3, 1);
this->evses[2]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[2]->connectors.at(1).get_state(), 2, 1);
// If we set even one more evse to unavailable (or actually, to faulted), this will cancel the next (or actually
// previous) reservation.
EXPECT_CALL(reservation_callback_mock, Call(_, 1, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[0]->connectors.at(1).get_state(), 0, 1);
EXPECT_FALSE(evse_id.has_value());
}
TEST_F(ReservationHandlerTest, change_availability_scenario_02) {
// Change availability of an EVSE and check if reservations are cancelled. This time, global and specific EVSE
// reservations mixed.
std::optional<uint32_t> evse_id;
MockFunction<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const ReservationEndReason reason, const bool send_reservation_update)>
reservation_callback_mock;
r.register_reservation_cancelled_callback(reservation_callback_mock.AsStdFunction());
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(3, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// Set an evse to not available, this will call the cancel reservation callback for the reservation of that evse id.
EXPECT_CALL(reservation_callback_mock, Call(_, 0, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
ASSERT_TRUE(evse_id.has_value());
EXPECT_EQ(evse_id.value(), 1);
// Setting an evse to faulted will cancel the next reservation (last made), this will be a 'global' reservation as
// there is no evse specific reservation made.
EXPECT_CALL(reservation_callback_mock, Call(_, 3, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[2]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[2]->connectors.at(1).get_state(), 2, 1);
EXPECT_FALSE(evse_id.has_value());
// Set one more evse to unavailable, this will cancel the next reservation.
EXPECT_CALL(reservation_callback_mock, Call(_, 2, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[0]->connectors.at(1).get_state(), 0, 1);
// r.set_evse_state(ConnectorState::FAULTED, 0);
EXPECT_FALSE(evse_id.has_value());
// Set the last evse to unavailable will cancel the reservation of that specific evse.
EXPECT_CALL(reservation_callback_mock, Call(_, 1, ReservationEndReason::Cancelled, true))
.WillOnce(SaveArg<0>(&evse_id));
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[3]->connectors.at(0).get_state(), 3, 1);
ASSERT_TRUE(evse_id.has_value());
EXPECT_EQ(evse_id.value(), 3);
}
TEST_F(ReservationHandlerTest, reservation_evse_unavailable) {
// Set evse unavailable and check if a reservation can not be made in that case. Global reservations.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[1]->connectors.at(0).get_state(), 1, 0);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
// r.set_evse_state(ConnectorState::UNAVAILABLE, 1);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
this->evses[0]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[0]->connectors.at(0).get_state(), 0, 0);
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[0]->connectors.at(0).get_state(), 0, 1);
this->evses[2]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[2]->connectors.at(0).get_state(), 2, 0);
this->evses[2]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[2]->connectors.at(0).get_state(), 2, 1);
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[3]->connectors.at(0).get_state(), 3, 1);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Unavailable);
}
TEST_F(ReservationHandlerTest, reservation_specific_evse_unavailable) {
// Set an EVSE to unavailable and check if that specific EVSE can not be reserved anymore.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Unavailable);
}
TEST_F(ReservationHandlerTest, reservation_specific_evse_faulted) {
// Set an EVSE to faulted and check if that specific EVSE can not be reserved anymore.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// Evse state is faulted, should return faulted.
this->evses[0]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[0]->connectors.at(0).get_state(), 0, 0);
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[0]->connectors.at(1).get_state(), 0, 1);
EXPECT_EQ(r.make_reservation(0, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Faulted);
// Connectors are faulted, should return faulted.
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(0).get_state(), 1, 0);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Faulted);
}
TEST_F(ReservationHandlerTest, reservation_evse_faulted) {
// Set EVSE's to faulted and check if no global reservations can made for that EVSE.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(0).get_state(), 1, 0);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
// One EVSE is faulted and there are only two cCCS2 connectors left. So only two global reservations for cCCS2 can
// be made.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// Everything is faulted now, a reservation is not possible anymore.
this->evses[0]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[0]->connectors.at(0).get_state(), 0, 0);
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[0]->connectors.at(1).get_state(), 0, 1);
this->evses[2]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[2]->connectors.at(0).get_state(), 2, 0);
this->evses[2]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[2]->connectors.at(1).get_state(), 2, 1);
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[3]->connectors.at(0).get_state(), 3, 1);
// All EVSE's are faulted, so 'Faulted' is returned.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Faulted);
}
TEST_F(ReservationHandlerTest, reservation_evse_unavailable_and_faulted) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// Set evse to faulted.
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(0).get_state(), 1, 0);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Occupied);
// Set all other evse's to unavailable, but not faulted.
this->evses[0]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[0]->connectors.at(0).get_state(), 0, 0);
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[0]->connectors.at(1).get_state(), 0, 1);
this->evses[2]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[2]->connectors.at(0).get_state(), 2, 0);
this->evses[2]->connectors.at(1).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[2]->connectors.at(1).get_state(), 2, 1);
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
r.on_connector_state_changed(this->evses[3]->connectors.at(0).get_state(), 3, 1);
// At least one evse is faulted, so 'faulted' is returned.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Faulted);
}
TEST_F(ReservationHandlerTest, reservation_connector_all_faulted) {
// Set all connectors to 'Faulted', no reservation can be made and the function will return 'Faulted'.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(3, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(3, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses[0]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
this->evses[0]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
this->evses[3]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
this->evses[3]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Faulted);
}
TEST_F(ReservationHandlerTest, reservation_connector_unavailable) {
// Set specific connectors to 'Unavailable' and try to make reservations.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS1, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType1, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses[0]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::DISABLE);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
// There is a reservation already made, so this will return 'occupied'.
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS1)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType1)),
ReservationResult::Occupied);
}
TEST_F(ReservationHandlerTest, reservation_in_the_past) {
// Try to create a reservation in the past, this should be rejected.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2);
reservation.expiry_time = Everest::Date::to_rfc3339(date::utc_clock::now() - std::chrono::hours(2));
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Rejected);
}
TEST_F(ReservationHandlerTest, reservation_timer) {
// Test the reservation timer: after the time has expired, the reservation should be cancelled.
std::optional<uint32_t> evse_id;
MockFunction<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const ReservationEndReason reason, const bool send_reservation_update)>
reservation_callback_mock;
r.register_reservation_cancelled_callback(reservation_callback_mock.AsStdFunction());
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_CALL(reservation_callback_mock, Call(_, 0, ReservationEndReason::Expired, true))
.WillOnce(SaveArg<0>(&evse_id));
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2);
reservation.expiry_time = Everest::Date::to_rfc3339(date::utc_clock::now() + std::chrono::seconds(1));
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Accepted);
sleep(2);
EXPECT_FALSE(evse_id.has_value());
EXPECT_CALL(reservation_callback_mock, Call(_, 0, ReservationEndReason::Expired, true))
.WillOnce(SaveArg<0>(&evse_id));
reservation.expiry_time = Everest::Date::to_rfc3339(date::utc_clock::now() + std::chrono::seconds(1));
EXPECT_EQ(r.make_reservation(0, reservation), ReservationResult::Accepted);
sleep(2);
ASSERT_TRUE(evse_id.has_value());
EXPECT_EQ(evse_id.value(), 0);
}
TEST_F(ReservationHandlerTest, cancel_reservation) {
// Cancel reservation and check if a new reservation can be made after an old one is cancelled.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
std::pair<bool, std::optional<uint32_t>> reservation_cancelled_check_value;
// There was no reservation with id 5.
reservation_cancelled_check_value = {false, std::nullopt};
EXPECT_EQ(r.cancel_reservation(5, false, ReservationEndReason::Cancelled), reservation_cancelled_check_value);
// There was a reservation with id 1, it had no evse id (global reservation).
reservation_cancelled_check_value = {true, std::nullopt};
EXPECT_EQ(r.cancel_reservation(1, false, ReservationEndReason::Cancelled), reservation_cancelled_check_value);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
// There was a reservation with id 3, it was made for evse id 1.
reservation_cancelled_check_value = {true, 1};
EXPECT_EQ(r.cancel_reservation(3, false, ReservationEndReason::Cancelled), reservation_cancelled_check_value);
}
TEST_F(ReservationHandlerTest, overwrite_reservation) {
// If a reservation is made and another one is made with the same reservation id, it should be overwritten.
// The old reservation will then be cancelled and the new one is made.
MockFunction<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const ReservationEndReason reason, const bool send_reservation_update)>
reservation_callback_mock;
r.register_reservation_cancelled_callback(reservation_callback_mock.AsStdFunction());
add_connector(5, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(5, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_CALL(reservation_callback_mock, Call(_, 0, ReservationEndReason::Cancelled, false)).Times(0);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
EXPECT_EQ(r.make_reservation(5, reservation), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(5, reservation), ReservationResult::Accepted);
}
TEST_F(ReservationHandlerTest, matches_reserved_identifier) {
// Check if token or parent token matches with a reservation.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(2, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(2, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation.parent_id_token = "PARENT_TOKEN_0";
Reservation reservation2 = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation2.parent_id_token = "PARENT_TOKEN_2";
Reservation reservation3 = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation3.parent_id_token = "PARENT_TOKEN_3";
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(1, reservation2), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(2, reservation3), ReservationResult::Accepted);
// Id token is correct and evse id as well.
EXPECT_EQ(r.matches_reserved_identifier(reservation.id_token, std::nullopt, std::nullopt), 0);
// Id token is correct and evse id as well, parent token is not but that is ignored since the normal token is ok.
EXPECT_EQ(r.matches_reserved_identifier(reservation.id_token, std::nullopt, "WRONG_PARENT_TOKEN"), 0);
// Token is wrong.
EXPECT_EQ(r.matches_reserved_identifier("WRONG_TOKEN", std::nullopt, std::nullopt), std::nullopt);
// Evse id reservation does not have parent token, do not search in global reservation.
EXPECT_EQ(r.matches_reserved_identifier(reservation.id_token, 1, std::nullopt), std::nullopt);
// Evse id is wrong.
EXPECT_EQ(r.matches_reserved_identifier(reservation2.id_token, 2, std::nullopt), std::nullopt);
// Token is wrong but parent token is correct.
EXPECT_EQ(r.matches_reserved_identifier("WRONG_TOKEN", std::nullopt, "PARENT_TOKEN_0"), 0);
// Token is wrong and parent token as well.
EXPECT_EQ(r.matches_reserved_identifier("WRONG_TOKEN", std::nullopt, "WRONG_PARENT_TOKEN"), std::nullopt);
// Evse id is correct and token is correct.
EXPECT_EQ(r.matches_reserved_identifier(reservation2.id_token, 1, std::nullopt), 1);
// Evse id is correct but token is wrong.
EXPECT_EQ(r.matches_reserved_identifier("TOKEN_NOK", 1, std::nullopt), std::nullopt);
// Evse id is wrong and token is correct.
EXPECT_EQ(r.matches_reserved_identifier(reservation2.id_token, 2, std::nullopt), std::nullopt);
// Evse id is correct, token is wrong but parent token is correct.
EXPECT_EQ(r.matches_reserved_identifier("TOKEN_NOK", 1, "PARENT_TOKEN_2"), 1);
// Evse id is correct, token is wrong and parent token as well.
EXPECT_EQ(r.matches_reserved_identifier("TOKEN_NOK", 1, "PARENT_TOKEN_NOK"), std::nullopt);
}
TEST_F(ReservationHandlerTest, has_reservation_parent_id) {
// Check if the reservation has a parent id token.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation.parent_id_token = "PARENT_TOKEN_0";
Reservation reservation2 = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation2.parent_id_token = "PARENT_TOKEN_2";
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(1, reservation2), ReservationResult::Accepted);
// Id token is correct and evse id as well.
EXPECT_TRUE(r.has_reservation_parent_id(std::nullopt));
EXPECT_TRUE(r.has_reservation_parent_id(1));
EXPECT_TRUE(r.has_reservation_parent_id(0));
// Evse id does not exist.
EXPECT_FALSE(r.has_reservation_parent_id(2));
}
TEST_F(ReservationHandlerTest, has_reservation_parent_id_no_parent_token) {
// Check if the reservation has a parent id token.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
Reservation reservation2 = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(1, reservation2), ReservationResult::Accepted);
// No parent id tokens
EXPECT_FALSE(r.has_reservation_parent_id(std::nullopt));
EXPECT_FALSE(r.has_reservation_parent_id(1));
EXPECT_FALSE(r.has_reservation_parent_id(0));
// Evse id does not exist.
EXPECT_FALSE(r.has_reservation_parent_id(2));
}
TEST_F(ReservationHandlerTest, has_reservation_parent_id_evse_reservation_parent_token) {
// Check if the reservation has a parent id token.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
Reservation reservation2 = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation2.parent_id_token = "PARENT_TOKEN_2";
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(1, reservation2), ReservationResult::Accepted);
// Only evse id 1 reservation has parent id token.
EXPECT_FALSE(r.has_reservation_parent_id(std::nullopt));
EXPECT_TRUE(r.has_reservation_parent_id(1));
// So evse id 0 has not.
EXPECT_FALSE(r.has_reservation_parent_id(0));
// Evse id does not exist.
EXPECT_FALSE(r.has_reservation_parent_id(2));
}
TEST_F(ReservationHandlerTest, has_reservation_parent_id_global_reservation_parent_token) {
// Check if the reservation has a parent id token.
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
Reservation reservation = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
reservation.parent_id_token = "PARENT_TOKEN_0";
Reservation reservation2 = create_reservation(types::evse_manager::ConnectorTypeEnum::cType2);
EXPECT_EQ(r.make_reservation(std::nullopt, reservation), ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(1, reservation2), ReservationResult::Accepted);
// Only global reservation has parent id token. Reservation on evse id 1 has none.
EXPECT_TRUE(r.has_reservation_parent_id(std::nullopt));
EXPECT_FALSE(r.has_reservation_parent_id(1));
// No reservation for evse id 0, but global reservation has parent id token.
EXPECT_TRUE(r.has_reservation_parent_id(0));
// Evse id does not exist.
EXPECT_FALSE(r.has_reservation_parent_id(2));
}
TEST_F(ReservationHandlerTest, on_reservation_used) {
// A reservation is made and later used, so the reservation should be removed and the EVSE available again.
// Register a callback, which should not be called.
MockFunction<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const ReservationEndReason reason, const bool send_reservation_update)>
reservation_callback_mock;
r.register_reservation_cancelled_callback(reservation_callback_mock.AsStdFunction());
EXPECT_CALL(reservation_callback_mock, Call(_, _, _, true)).Times(0);
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
r.on_reservation_used(1);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Occupied);
r.on_reservation_used(0);
r.on_reservation_used(2);
r.on_reservation_used(3);
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
}
TEST_F(ReservationHandlerTest, store_load_reservations) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
EXPECT_TRUE(r.evse_reservations.empty());
EXPECT_TRUE(r.global_reservations.empty());
r.load_reservations();
EXPECT_TRUE(r.evse_reservations.empty());
EXPECT_TRUE(r.global_reservations.empty());
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.evse_reservations.size(), 1);
EXPECT_EQ(r.global_reservations.size(), 1);
EXPECT_EQ(r.reservation_id_to_reservation_timeout_timer_map.size(), 2);
r.evse_reservations.clear();
r.global_reservations.clear();
r.reservation_id_to_reservation_timeout_timer_map.clear();
EXPECT_TRUE(r.evse_reservations.empty());
EXPECT_TRUE(r.global_reservations.empty());
EXPECT_TRUE(r.reservation_id_to_reservation_timeout_timer_map.empty());
r.load_reservations();
EXPECT_EQ(r.evse_reservations.size(), 1);
EXPECT_EQ(r.global_reservations.size(), 1);
EXPECT_EQ(r.reservation_id_to_reservation_timeout_timer_map.size(), 2);
}
TEST_F(ReservationHandlerTest, store_load_reservations_connector_unavailable) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
// Register a callback, which should not be called.
MockFunction<void(const std::optional<uint32_t>& evse_id, const int32_t reservation_id,
const ReservationEndReason reason, const bool send_reservation_update)>
reservation_callback_mock;
r.register_reservation_cancelled_callback(reservation_callback_mock.AsStdFunction());
EXPECT_CALL(reservation_callback_mock, Call(_, _, _, true)).Times(1);
EXPECT_TRUE(r.evse_reservations.empty());
EXPECT_TRUE(r.global_reservations.empty());
r.load_reservations();
EXPECT_TRUE(r.evse_reservations.empty());
EXPECT_TRUE(r.global_reservations.empty());
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
EXPECT_EQ(r.evse_reservations.size(), 1);
EXPECT_EQ(r.global_reservations.size(), 1);
EXPECT_EQ(r.reservation_id_to_reservation_timeout_timer_map.size(), 2);
r.evse_reservations.clear();
r.global_reservations.clear();
r.reservation_id_to_reservation_timeout_timer_map.clear();
EXPECT_TRUE(r.evse_reservations.empty());
EXPECT_TRUE(r.global_reservations.empty());
EXPECT_TRUE(r.reservation_id_to_reservation_timeout_timer_map.empty());
this->evses[1]->connectors.at(0).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(0).get_state(), 3, 0);
this->evses[1]->connectors.at(1).submit_event(ConnectorEvent::FAULTED);
r.on_connector_state_changed(this->evses[1]->connectors.at(1).get_state(), 1, 1);
r.load_reservations();
EXPECT_EQ(r.evse_reservations.size(), 0);
EXPECT_EQ(r.global_reservations.size(), 1);
EXPECT_EQ(r.reservation_id_to_reservation_timeout_timer_map.size(), 1);
}
TEST_F(ReservationHandlerTest, check_evses_to_reserve_scenario_1) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
ReservationEvseStatus s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(1, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
ASSERT_EQ(s.reserved.size(), 1);
EXPECT_EQ(s.reserved.count(0), 1);
}
TEST_F(ReservationHandlerTest, check_evses_to_reserve_scenario_2) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
ReservationEvseStatus s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
ASSERT_EQ(s.reserved.size(), 2);
EXPECT_EQ(s.reserved.count(0), 1);
EXPECT_EQ(s.reserved.count(1), 1);
}
TEST_F(ReservationHandlerTest, check_evses_to_reserve_scenario_3) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
ReservationEvseStatus s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
ASSERT_EQ(s.reserved.size(), 1);
EXPECT_EQ(s.reserved.count(1), 1);
}
TEST_F(ReservationHandlerTest, check_evses_to_reserve_scenario_4) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses.at(0)->plugged_in = true;
ReservationEvseStatus s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
ASSERT_EQ(s.reserved.size(), 1);
EXPECT_EQ(s.reserved.count(1), 1);
this->evses.at(0)->plugged_in = false;
s = r.check_number_global_reservations_match_number_available_evses();
ASSERT_EQ(s.available.size(), 1);
EXPECT_EQ(s.available.count(1), 1);
EXPECT_TRUE(s.reserved.empty());
}
TEST_F(ReservationHandlerTest, check_evses_to_reserve_scenario_5) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
this->evses.at(1)->connectors.at(0).submit_event(ConnectorEvent::TRANSACTION_STARTED);
ReservationEvseStatus s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cType2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
ASSERT_EQ(s.reserved.size(), 1);
EXPECT_EQ(s.reserved.count(0), 1);
this->evses.at(1)->connectors.at(0).submit_event(ConnectorEvent::SESSION_FINISHED);
s = r.check_number_global_reservations_match_number_available_evses();
ASSERT_EQ(s.available.size(), 1);
EXPECT_EQ(s.available.count(0), 1);
EXPECT_TRUE(s.reserved.empty());
}
TEST_F(ReservationHandlerTest, check_evses_to_reserve_scenario_6) {
add_connector(0, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(0, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
add_connector(1, 0, types::evse_manager::ConnectorTypeEnum::cCCS2, this->evses);
add_connector(1, 1, types::evse_manager::ConnectorTypeEnum::cType2, this->evses);
ReservationEvseStatus s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
Reservation reservation1 = create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2);
EXPECT_EQ(r.make_reservation(std::nullopt, reservation1), ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
EXPECT_TRUE(s.reserved.empty());
EXPECT_EQ(r.make_reservation(std::nullopt, create_reservation(types::evse_manager::ConnectorTypeEnum::cCCS2)),
ReservationResult::Accepted);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_TRUE(s.available.empty());
ASSERT_EQ(s.reserved.size(), 2);
EXPECT_EQ(s.reserved.count(0), 1);
EXPECT_EQ(s.reserved.count(1), 1);
r.cancel_reservation(reservation1.reservation_id, false, types::reservation::ReservationEndReason::Cancelled);
s = r.check_number_global_reservations_match_number_available_evses();
EXPECT_EQ(s.available.size(), 2);
EXPECT_EQ(s.reserved.size(), 0);
}
} // namespace module