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
This commit is contained in:
212
tools/EVerest-main/modules/EVSE/EvseV2G/tests/CMakeLists.txt
Normal file
212
tools/EVerest-main/modules/EVSE/EvseV2G/tests/CMakeLists.txt
Normal file
@@ -0,0 +1,212 @@
|
||||
get_target_property(GENERATED_INCLUDE_DIR generate_cpp_files EVEREST_GENERATED_INCLUDE_DIR)
|
||||
find_package(libevent)
|
||||
find_package(OpenSSL 3)
|
||||
|
||||
set(LIB_EVEREST_TLS_TESTS_DIR "${PROJECT_SOURCE_DIR}/lib/everest/tls/tests")
|
||||
set(TESTS_INCLUDE_DIR "${PROJECT_SOURCE_DIR}/tests/include")
|
||||
|
||||
set(TLS_TEST_FILES
|
||||
alt_openssl-pki.conf
|
||||
iso_pkey.asn1
|
||||
openssl-pki.conf
|
||||
ocsp_response.der
|
||||
pki.sh
|
||||
)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${TLS_TEST_FILES}
|
||||
COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/pki
|
||||
COMMAND cd pki && cp ${TLS_TEST_FILES} ${CMAKE_CURRENT_BINARY_DIR}/
|
||||
WORKING_DIRECTORY ${LIB_EVEREST_TLS_TESTS_DIR}
|
||||
)
|
||||
|
||||
add_custom_target(v2g_test_files_target
|
||||
DEPENDS ${TLS_TEST_FILES}
|
||||
)
|
||||
|
||||
set(TLS_GTEST_NAME v2g_openssl_test)
|
||||
add_executable(${TLS_GTEST_NAME})
|
||||
add_dependencies(${TLS_GTEST_NAME} v2g_test_files_target)
|
||||
|
||||
add_dependencies(${TLS_GTEST_NAME} generate_cpp_files)
|
||||
|
||||
target_include_directories(${TLS_GTEST_NAME} PRIVATE
|
||||
.. ../crypto
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
${CMAKE_BINARY_DIR}/generated/modules/${MODULE_NAME}
|
||||
)
|
||||
|
||||
target_compile_definitions(${TLS_GTEST_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
)
|
||||
|
||||
target_sources(${TLS_GTEST_NAME} PRIVATE
|
||||
${LIB_EVEREST_TLS_TESTS_DIR}/gtest_main.cpp
|
||||
log.cpp
|
||||
openssl_test.cpp
|
||||
../crypto/crypto_openssl.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${TLS_GTEST_NAME} PRIVATE
|
||||
GTest::gtest
|
||||
cbv2g::din
|
||||
cbv2g::iso2
|
||||
cbv2g::tp
|
||||
everest::framework
|
||||
everest::evse_security
|
||||
everest::tls
|
||||
)
|
||||
|
||||
set(V2G_MAIN_NAME v2g_server)
|
||||
add_executable(${V2G_MAIN_NAME})
|
||||
|
||||
add_dependencies(${V2G_MAIN_NAME} generate_cpp_files)
|
||||
|
||||
target_include_directories(${V2G_MAIN_NAME} PRIVATE
|
||||
.. ../connection ${TESTS_INCLUDE_DIR}
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
${CMAKE_BINARY_DIR}/generated/modules/${MODULE_NAME}
|
||||
${CMAKE_BINARY_DIR}/generated/include
|
||||
)
|
||||
|
||||
target_compile_definitions(${V2G_MAIN_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
)
|
||||
|
||||
target_sources(${V2G_MAIN_NAME} PRIVATE
|
||||
../connection/connection.cpp
|
||||
../connection/tls_connection.cpp
|
||||
../tools.cpp
|
||||
../v2g_ctx.cpp
|
||||
log.cpp
|
||||
requirement.cpp
|
||||
v2g_main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${V2G_MAIN_NAME} PRIVATE
|
||||
cbv2g::din
|
||||
cbv2g::iso2
|
||||
cbv2g::tp
|
||||
everest::log
|
||||
everest::framework
|
||||
everest::evse_security
|
||||
everest::tls
|
||||
-levent -lpthread -levent_pthreads
|
||||
)
|
||||
|
||||
# runs fine locally, fails in CI
|
||||
add_test(${TLS_GTEST_NAME} ${TLS_GTEST_NAME})
|
||||
ev_register_test_target(${TLS_GTEST_NAME})
|
||||
|
||||
|
||||
set(DIN_SERVER_NAME din_server_test)
|
||||
add_executable(${DIN_SERVER_NAME})
|
||||
|
||||
target_include_directories(${DIN_SERVER_NAME} PRIVATE
|
||||
.. ../connection ${TESTS_INCLUDE_DIR}
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
${CMAKE_BINARY_DIR}/generated/modules/${MODULE_NAME}
|
||||
${CMAKE_BINARY_DIR}/generated/include
|
||||
)
|
||||
add_dependencies(${DIN_SERVER_NAME} generate_cpp_files)
|
||||
|
||||
target_compile_definitions(${DIN_SERVER_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
-DLIBEVSE_CRYPTO_SUPPLIER_OPENSSL
|
||||
)
|
||||
|
||||
target_sources(${DIN_SERVER_NAME} PRIVATE
|
||||
din_server_test.cpp
|
||||
log.cpp
|
||||
../din_server.cpp
|
||||
../tools.cpp # TODO: Maybe mock this one
|
||||
)
|
||||
|
||||
target_link_libraries(${DIN_SERVER_NAME}
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
cbv2g::din
|
||||
cbv2g::iso2
|
||||
cbv2g::tp
|
||||
everest::framework
|
||||
everest::evse_security
|
||||
everest::tls
|
||||
)
|
||||
|
||||
add_test(${DIN_SERVER_NAME} ${DIN_SERVER_NAME})
|
||||
ev_register_test_target(${DIN_SERVER_NAME})
|
||||
|
||||
set(SDP_NAME sdp_test)
|
||||
add_executable(${SDP_NAME})
|
||||
target_include_directories(${SDP_NAME} PRIVATE
|
||||
.. ../connection ${TESTS_INCLUDE_DIR}
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
${CMAKE_BINARY_DIR}/generated/modules/${MODULE_NAME}
|
||||
${CMAKE_BINARY_DIR}/generated/include
|
||||
)
|
||||
add_dependencies(${SDP_NAME} generate_cpp_files)
|
||||
|
||||
target_compile_definitions(${SDP_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
)
|
||||
|
||||
target_sources(${SDP_NAME} PRIVATE
|
||||
sdp_test.cpp
|
||||
log.cpp
|
||||
../sdp.cpp
|
||||
../tools.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${SDP_NAME}
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
cbv2g::tp
|
||||
everest::framework
|
||||
everest::tls
|
||||
)
|
||||
|
||||
add_test(${SDP_NAME} ${SDP_NAME})
|
||||
ev_register_test_target(${SDP_NAME})
|
||||
|
||||
set(V2GCTX_NAME v2g_ctx_test)
|
||||
add_executable(${V2GCTX_NAME})
|
||||
|
||||
target_include_directories(${V2GCTX_NAME} PRIVATE
|
||||
.. ../connection ${TESTS_INCLUDE_DIR}
|
||||
${GENERATED_INCLUDE_DIR}
|
||||
${CMAKE_BINARY_DIR}/generated/modules/${MODULE_NAME}
|
||||
${CMAKE_BINARY_DIR}/generated/include
|
||||
)
|
||||
add_dependencies(${V2GCTX_NAME} generate_cpp_files)
|
||||
|
||||
target_compile_definitions(${V2GCTX_NAME} PRIVATE
|
||||
-DUNIT_TEST
|
||||
-DLIBEVSE_CRYPTO_SUPPLIER_OPENSSL
|
||||
)
|
||||
|
||||
target_sources(${V2GCTX_NAME} PRIVATE
|
||||
v2g_ctx_test.cpp
|
||||
log.cpp
|
||||
tools_test.cpp
|
||||
../v2g_ctx.cpp
|
||||
../tools.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(${V2GCTX_NAME}
|
||||
PRIVATE
|
||||
GTest::gtest_main
|
||||
OpenSSL::SSL
|
||||
OpenSSL::Crypto
|
||||
cbv2g::din
|
||||
cbv2g::iso2
|
||||
cbv2g::tp
|
||||
everest::framework
|
||||
everest::evse_security
|
||||
everest::tls
|
||||
-levent -lpthread -levent_pthreads
|
||||
)
|
||||
|
||||
add_test(${V2GCTX_NAME} ${V2GCTX_NAME})
|
||||
ev_register_test_target(${V2GCTX_NAME})
|
||||
@@ -0,0 +1,121 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef ISO15118_CHARGERIMPLSTUB_H_
|
||||
#define ISO15118_CHARGERIMPLSTUB_H_
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
#include "ModuleAdapterStub.hpp"
|
||||
|
||||
#include <generated/interfaces/ISO15118_charger/Implementation.hpp>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace module::stub {
|
||||
|
||||
struct ISO15118_chargerImplStub : public ISO15118_chargerImplBase {
|
||||
ISO15118_chargerImplStub(ModuleAdapterStub& adapter) : ISO15118_chargerImplBase(&adapter, "EvseV2G"){};
|
||||
ISO15118_chargerImplStub(ModuleAdapterStub* adapter) : ISO15118_chargerImplBase(adapter, "EvseV2G"){};
|
||||
|
||||
virtual void init() {
|
||||
}
|
||||
virtual void ready() {
|
||||
}
|
||||
|
||||
virtual void handle_setup(types::iso15118::EVSEID& evse_id, types::iso15118::SaeJ2847BidiMode& sae_j2847_mode,
|
||||
bool& debug_mode) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_setup called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_energy_transfer_modes(
|
||||
std::vector<types::iso15118::EnergyTransferMode>& supported_energy_transfer_modes) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_energy_transfer_modes called" << std::endl;
|
||||
}
|
||||
virtual void handle_set_charging_parameters(types::iso15118::SetupPhysicalValues& physical_values) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_set_charging_parameters called" << std::endl;
|
||||
}
|
||||
virtual void handle_session_setup(std::vector<types::iso15118::PaymentOption>& payment_options,
|
||||
bool& supported_certificate_service, bool& central_contract_validation_allowed) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_session_setup called" << std::endl;
|
||||
}
|
||||
virtual void handle_authorization_response(types::authorization::AuthorizationStatus& authorization_status,
|
||||
types::authorization::CertificateStatus& certificate_status) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_authorization_response called" << std::endl;
|
||||
}
|
||||
virtual void handle_bpt_setup(types::iso15118::BptSetup& config) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_bpt_setup called" << std::endl;
|
||||
}
|
||||
virtual void handle_set_powersupply_capabilities(types::power_supply_DC::Capabilities& capabilities) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_set_powersupply_capabilities called" << std::endl;
|
||||
}
|
||||
virtual void handle_ac_contactor_closed(bool& status) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_ac_contactor_closed called" << std::endl;
|
||||
}
|
||||
virtual void handle_dlink_ready(bool& value) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_dlink_ready called" << std::endl;
|
||||
}
|
||||
virtual void handle_cable_check_finished(bool& status) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_cable_check_finished called" << std::endl;
|
||||
}
|
||||
virtual void handle_receipt_is_required(bool& receipt_required) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_receipt_is_required called" << std::endl;
|
||||
}
|
||||
virtual void handle_stop_charging(bool& stop) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_stop_charging called" << std::endl;
|
||||
}
|
||||
virtual void handle_pause_charging(bool& pause) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_pause_charging called" << std::endl;
|
||||
}
|
||||
virtual void handle_no_energy_pause_charging(types::iso15118::NoEnergyPauseMode& mode) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_no_energy_pause_charging called" << std::endl;
|
||||
}
|
||||
virtual bool
|
||||
handle_update_supported_app_protocols(types::iso15118::SupportedAppProtocols& supported_app_protocols) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_supported_app_protocols called" << std::endl;
|
||||
return true;
|
||||
}
|
||||
virtual void handle_update_ac_max_current(double& max_current) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_ac_max_current called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_ac_parameters(types::iso15118::AcParameters& ac_parameters) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_ac_parameters called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_ac_maximum_limits(types::iso15118::AcEvseMaximumPower& maximum_limits) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_ac_maximum_limits called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_ac_minimum_limits(types::iso15118::AcEvseMinimumPower& minimum_limits) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_ac_minimum_limits called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_ac_target_values(types::iso15118::AcTargetValues& target_values) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_ac_target_values called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_ac_present_power(types::units::Power& present_power) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_ac_present_power called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_dc_maximum_limits(types::iso15118::DcEvseMaximumLimits& maximum_limits) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_dc_maximum_limits called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_dc_minimum_limits(types::iso15118::DcEvseMinimumLimits& minimum_limits) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_dc_minimum_limits called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_isolation_status(types::iso15118::IsolationStatus& isolation_status) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_isolation_status called" << std::endl;
|
||||
}
|
||||
virtual void
|
||||
handle_update_dc_present_values(types::iso15118::DcEvsePresentVoltageCurrent& present_voltage_current) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_dc_present_values called" << std::endl;
|
||||
}
|
||||
virtual void handle_update_meter_info(types::powermeter::Powermeter& powermeter) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_update_meter_info called" << std::endl;
|
||||
}
|
||||
virtual void handle_send_error(types::iso15118::EvseError& error) {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_send_error called" << std::endl;
|
||||
}
|
||||
virtual void handle_reset_error() {
|
||||
std::cout << "ISO15118_chargerImplBase::handle_reset_error called" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace module::stub
|
||||
|
||||
#endif // ISO15118_CHARGERIMPLSTUB_H_
|
||||
51
tools/EVerest-main/modules/EVSE/EvseV2G/tests/README.md
Normal file
51
tools/EVerest-main/modules/EVSE/EvseV2G/tests/README.md
Normal file
@@ -0,0 +1,51 @@
|
||||
|
||||
# Tests
|
||||
|
||||
Building tests:
|
||||
|
||||
```sh
|
||||
$ cd EVerest
|
||||
$ mkdir build
|
||||
$ cd build
|
||||
$ cmake -GNinja -DEVEREST_CORE_BUILD_TESTING=ON ..
|
||||
$ ninja install
|
||||
```
|
||||
|
||||
`touch release.json` may be needed if it hasn't been created
|
||||
(then re-run `ninja install`).
|
||||
|
||||
## Run EVerest in SIL
|
||||
|
||||
1. start MQTT broker
|
||||
2. from `build/run-scripts` run `./run-sil-dc-tls.sh`
|
||||
3. from `build/run-scripts` run `./nodered-sil-dc.sh`
|
||||
4. open web browser [EVerest Node-RED dashboard](http://localhost:1880/ui/)
|
||||
|
||||
## Unit tests
|
||||
|
||||
- `./v2g_openssl_test`
|
||||
- automatically runs `pki.sh`
|
||||
- run from the directory containing the executable
|
||||
|
||||
### Standalone V2G TLS server
|
||||
|
||||
Tests the Server class via the functions in connection.cpp and
|
||||
tls_connection.cpp.
|
||||
|
||||
- `./v2g_server -i <interface name>`
|
||||
- connects to IPv6 only with a link local address
|
||||
- requires `boost` library so LD_LIBRARY_PATH may need to be set
|
||||
- displays the address it is listening on. e.g.
|
||||
`[fe80::ae91:a1ff:fec9:a947%3]:64109`
|
||||
- supports multiple connections
|
||||
- gracefully terminates after 80 seconds
|
||||
- `valgrind` can be used to check memory allocations
|
||||
(has some leaks - possibly in v2g_ctx_start_events thread)
|
||||
- requires client certificate
|
||||
- s_client echos back what is typed with a delay since V2G has a long timeout
|
||||
|
||||
The connect argument must match what was displayed by `v2g_server`
|
||||
|
||||
```sh
|
||||
openssl s_client -connect [fe80::ae91:a1ff:fec9:a947%3]:64109 -verify 2 -CAfile server_root_cert.pem -cert client_cert.pem -cert_chain client_chain.pem -key client_priv.pem -verify_return_error -verify_hostname evse.pionix.de -status
|
||||
```
|
||||
@@ -0,0 +1,563 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include <din_server.hpp>
|
||||
|
||||
#include <cstring>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "ISO15118_chargerImplStub.hpp"
|
||||
#include "cbv2g/din/din_msgDefDatatypes.h"
|
||||
#include "utest_log.hpp"
|
||||
#include "v2g.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
void publish_dc_ev_maximum_limits(struct v2g_context* ctx, const float& v2g_dc_ev_max_current_limit,
|
||||
const unsigned int& v2g_dc_ev_max_current_limit_is_used,
|
||||
const float& v2g_dc_ev_max_power_limit,
|
||||
const unsigned int& v2g_dc_ev_max_power_limit_is_used,
|
||||
const float& v2g_dc_ev_max_voltage_limit,
|
||||
const unsigned int& v2g_dc_ev_max_voltage_limit_is_used) {
|
||||
}
|
||||
|
||||
void stop_timer(struct event** event_timer, char const* const timer_name, struct v2g_context* ctx) {
|
||||
}
|
||||
|
||||
void log_selected_energy_transfer_type(int selected_energy_transfer_mode) {
|
||||
}
|
||||
|
||||
uint64_t v2g_session_id_from_exi(bool is_iso, void* exi_in) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void publish_dc_ev_target_voltage_current(struct v2g_context* ctx, const float& v2g_dc_ev_target_voltage,
|
||||
const float& v2g_dc_ev_target_current) {
|
||||
}
|
||||
|
||||
void publish_dc_ev_remaining_time(struct v2g_context* ctx, const float& v2g_dc_ev_remaining_time_to_full_soc,
|
||||
const unsigned int& v2g_dc_ev_remaining_time_to_full_soc_is_used,
|
||||
const float& v2g_dc_ev_remaining_time_to_bulk_soc,
|
||||
const unsigned int& v2g_dc_ev_remaining_time_to_bulk_soc_is_used) {
|
||||
}
|
||||
|
||||
namespace {
|
||||
class DinServerTest : public testing::Test {
|
||||
protected:
|
||||
std::unique_ptr<v2g_connection> conn;
|
||||
std::unique_ptr<v2g_context> ctx;
|
||||
std::unique_ptr<din_exiDocument> exi_in;
|
||||
std::unique_ptr<din_exiDocument> exi_out;
|
||||
|
||||
module::stub::ModuleAdapterStub adapter;
|
||||
module::stub::ISO15118_chargerImplStub charger;
|
||||
|
||||
DinServerTest() : charger(adapter) {
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
conn = std::make_unique<v2g_connection>();
|
||||
ctx = std::make_unique<v2g_context>();
|
||||
exi_in = std::make_unique<din_exiDocument>();
|
||||
exi_out = std::make_unique<din_exiDocument>();
|
||||
|
||||
module::stub::clear_logs();
|
||||
conn->ctx = ctx.get();
|
||||
conn->ctx->p_charger = &charger;
|
||||
|
||||
conn->exi_in.dinEXIDocument = exi_in.get();
|
||||
conn->exi_out.dinEXIDocument = exi_out.get();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
}
|
||||
};
|
||||
|
||||
class DinServerTestValidateResponseCode
|
||||
: public DinServerTest,
|
||||
public testing::WithParamInterface<
|
||||
std::tuple<int /*din_responseCodeType*/, bool, bool, bool, int /*V2gMsgTypeId*/, uint64_t, uint64_t, bool>> {
|
||||
};
|
||||
|
||||
// For all test cases:
|
||||
// TODO: Define helper functions to set the conn and ctx variables
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
// Potential test for SessionSetup:
|
||||
// Bad Case:
|
||||
// Setting no EvseID -> A check should be added -> But a default value is in ctx provided.
|
||||
TEST_F(DinServerTest, session_setup_generating_new_session_id) {
|
||||
// Setting up session_setup_req
|
||||
auto& session_setup_req = exi_in->V2G_Message.Body.SessionSetupReq;
|
||||
exi_in->V2G_Message.Body.SessionSetupReq_isUsed = true;
|
||||
init_din_SessionSetupReqType(&session_setup_req);
|
||||
|
||||
const uint8_t evcc_id[8] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
|
||||
memcpy(session_setup_req.EVCCID.bytes, evcc_id, sizeof(evcc_id));
|
||||
session_setup_req.EVCCID.bytesLen = sizeof(evcc_id);
|
||||
|
||||
// Setting up conn
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG;
|
||||
|
||||
ctx->evse_v2g_data.session_id = 0;
|
||||
ctx->evse_v2g_data.date_time_now_is_used = 0;
|
||||
|
||||
ctx->ev_v2g_data.received_session_id = 0;
|
||||
|
||||
std::string evse_id = std::string("DE*PNX*TET1*234");
|
||||
strcpy(reinterpret_cast<char*>(ctx->evse_v2g_data.evse_id.bytes), evse_id.data());
|
||||
ctx->evse_v2g_data.evse_id.bytesLen = evse_id.size();
|
||||
|
||||
// Setting up session_setup_res
|
||||
auto& session_setup_res = exi_out->V2G_Message.Body.SessionSetupRes;
|
||||
exi_out->V2G_Message.Body.SessionSetupRes_isUsed = 1u;
|
||||
init_din_SessionSetupResType(&session_setup_res);
|
||||
|
||||
EXPECT_EQ(states::handle_din_session_setup(conn.get()), V2G_EVENT_NO_EVENT);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_ERROR).size(), 0);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_INFO).size(), 3);
|
||||
|
||||
EXPECT_EQ(session_setup_res.DateTimeNow_isUsed, false);
|
||||
// Checking if session id is generated
|
||||
EXPECT_GT(ctx->evse_v2g_data.session_id, 0);
|
||||
// Checking if evse id was set correctly
|
||||
EXPECT_EQ(evse_id, std::string(reinterpret_cast<char*>(session_setup_res.EVSEID.bytes)));
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, session_setup_old_session_id) {
|
||||
// Setting up session_setup_req
|
||||
auto& session_setup_req = exi_in->V2G_Message.Body.SessionSetupReq;
|
||||
exi_in->V2G_Message.Body.SessionSetupReq_isUsed = true;
|
||||
init_din_SessionSetupReqType(&session_setup_req);
|
||||
|
||||
const uint8_t evcc_id[8] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
|
||||
memcpy(session_setup_req.EVCCID.bytes, evcc_id, sizeof(evcc_id));
|
||||
session_setup_req.EVCCID.bytesLen = sizeof(evcc_id);
|
||||
|
||||
// Setting up conn
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG;
|
||||
|
||||
ctx->evse_v2g_data.session_id = 4158610156;
|
||||
ctx->evse_v2g_data.date_time_now_is_used = 0;
|
||||
|
||||
ctx->ev_v2g_data.received_session_id = 0;
|
||||
|
||||
std::string evse_id = std::string("DE*PNX*TET1*234");
|
||||
strcpy(reinterpret_cast<char*>(ctx->evse_v2g_data.evse_id.bytes), evse_id.data());
|
||||
ctx->evse_v2g_data.evse_id.bytesLen = evse_id.size();
|
||||
|
||||
// Setting up session_setup_res
|
||||
auto& session_setup_res = exi_out->V2G_Message.Body.SessionSetupRes;
|
||||
exi_out->V2G_Message.Body.SessionSetupRes_isUsed = 1u;
|
||||
init_din_SessionSetupResType(&session_setup_res);
|
||||
|
||||
EXPECT_EQ(states::handle_din_session_setup(conn.get()), V2G_EVENT_NO_EVENT);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_ERROR).size(), 0);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_INFO).size(), 2);
|
||||
|
||||
EXPECT_EQ(session_setup_res.DateTimeNow_isUsed, false);
|
||||
// Checking if session id is generated
|
||||
EXPECT_EQ(ctx->evse_v2g_data.session_id, 4158610156);
|
||||
// Checking if evse id was set correctly
|
||||
EXPECT_EQ(evse_id, std::string(reinterpret_cast<char*>(session_setup_res.EVSEID.bytes)));
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, session_setup_datetime_is_used) {
|
||||
// Setting up session_setup_req
|
||||
auto& session_setup_req = exi_in->V2G_Message.Body.SessionSetupReq;
|
||||
exi_in->V2G_Message.Body.SessionSetupReq_isUsed = true;
|
||||
init_din_SessionSetupReqType(&session_setup_req);
|
||||
|
||||
const uint8_t evcc_id[8] = {0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11};
|
||||
memcpy(session_setup_req.EVCCID.bytes, evcc_id, sizeof(evcc_id));
|
||||
session_setup_req.EVCCID.bytesLen = sizeof(evcc_id);
|
||||
|
||||
// Setting up conn
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG;
|
||||
|
||||
ctx->evse_v2g_data.session_id = 0;
|
||||
ctx->evse_v2g_data.date_time_now_is_used = true;
|
||||
|
||||
ctx->ev_v2g_data.received_session_id = 0;
|
||||
|
||||
std::string evse_id = std::string("DE*PNX*TET1*234");
|
||||
strcpy(reinterpret_cast<char*>(ctx->evse_v2g_data.evse_id.bytes), evse_id.data());
|
||||
ctx->evse_v2g_data.evse_id.bytesLen = evse_id.size();
|
||||
|
||||
// Setting up session_setup_res
|
||||
auto& session_setup_res = exi_out->V2G_Message.Body.SessionSetupRes;
|
||||
exi_out->V2G_Message.Body.SessionSetupRes_isUsed = 1u;
|
||||
init_din_SessionSetupResType(&session_setup_res);
|
||||
|
||||
EXPECT_EQ(states::handle_din_session_setup(conn.get()), V2G_EVENT_NO_EVENT);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_ERROR).size(), 0);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_INFO).size(), 3);
|
||||
|
||||
EXPECT_EQ(session_setup_res.DateTimeNow_isUsed, true);
|
||||
EXPECT_GT(session_setup_res.DateTimeNow, 0);
|
||||
// Checking if session id is generated
|
||||
EXPECT_GT(ctx->evse_v2g_data.session_id, 0);
|
||||
// Checking if evse id was set correctly
|
||||
EXPECT_EQ(evse_id, std::string(reinterpret_cast<char*>(session_setup_res.EVSEID.bytes)));
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_service_discovery_good_case) {
|
||||
|
||||
// TODO(sl): Maybe add this to check exi_out proberly
|
||||
exi_out->V2G_Message.Body.ServiceDiscoveryRes_isUsed = true;
|
||||
init_din_ServiceDiscoveryResType(&exi_out->V2G_Message.Body.ServiceDiscoveryRes);
|
||||
|
||||
// TODO: Setting the correct session_id + received_session_id via functions
|
||||
|
||||
EXPECT_EQ(states::handle_din_service_discovery(conn.get()), V2G_EVENT_NO_EVENT);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_ERROR).size(), 1);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_INFO).size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, handle_din_contract_authentication_check_evse_processing_finished) {
|
||||
|
||||
// TODO: set a prober session id
|
||||
ctx->evse_v2g_data.session_id = 0;
|
||||
ctx->evse_v2g_data.date_time_now_is_used = 0;
|
||||
|
||||
ctx->current_v2g_msg = V2G_AUTHORIZATION_MSG;
|
||||
ctx->ev_v2g_data.received_session_id = 0;
|
||||
|
||||
ctx->evse_v2g_data.evse_processing[PHASE_AUTH] = 0;
|
||||
EXPECT_EQ(states::handle_din_contract_authentication(conn.get()), V2G_EVENT_NO_EVENT); // TODO
|
||||
|
||||
auto& res = exi_out->V2G_Message.Body.ContractAuthenticationRes;
|
||||
|
||||
// EXPECT_EQ(res.ResponseCode, din_responseCodeType_OK);
|
||||
EXPECT_EQ(res.EVSEProcessing, din_EVSEProcessingType_Finished);
|
||||
EXPECT_EQ(ctx->state, WAIT_FOR_CHARGEPARAMETERDISCOVERY);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_ERROR).size(), 1);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_INFO).size(), 0);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, handle_din_contract_authentication_check_evse_processing_ongoing) {
|
||||
|
||||
// TODO: set a prober session id
|
||||
ctx->evse_v2g_data.session_id = 0;
|
||||
ctx->evse_v2g_data.date_time_now_is_used = 0;
|
||||
|
||||
ctx->current_v2g_msg = V2G_AUTHORIZATION_MSG;
|
||||
ctx->ev_v2g_data.received_session_id = 0;
|
||||
|
||||
ctx->evse_v2g_data.evse_processing[PHASE_AUTH] = 1;
|
||||
EXPECT_EQ(states::handle_din_contract_authentication(conn.get()), V2G_EVENT_NO_EVENT); // TODO
|
||||
|
||||
auto& res = exi_out->V2G_Message.Body.ContractAuthenticationRes;
|
||||
|
||||
// EXPECT_EQ(res.ResponseCode, din_responseCodeType_OK);
|
||||
EXPECT_EQ(res.EVSEProcessing, din_EVSEProcessingType_Ongoing);
|
||||
EXPECT_EQ(ctx->state, WAIT_FOR_AUTHORIZATION);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_ERROR).size(), 1);
|
||||
EXPECT_EQ(module::stub::get_logs(dloglevel_t::DLOG_LEVEL_INFO).size(), 0);
|
||||
}
|
||||
|
||||
// if not otherwise specified, the following testcases are happy paths
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_TERMINATE_CONNECTION) {
|
||||
|
||||
// which response code is actually irrelevant here and was picked at random
|
||||
auto tmp = din_responseCodeType_FAILED_TariffSelectionInvalid;
|
||||
|
||||
// only this bool determines the outcome
|
||||
ctx->is_connection_terminated = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_TERMINATE_CONNECTION);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_failed_response_FAILED) {
|
||||
|
||||
// which response code is actually irrelevant here and was picked at random
|
||||
// FAILED code
|
||||
auto tmp = din_responseCodeType_FAILED_ChallengeInvalid;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = false;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_failed_response_OK) {
|
||||
|
||||
// which response code is actually irrelevant here and was picked at random
|
||||
// OK code
|
||||
auto tmp = din_responseCodeType_OK;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = false;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_OK) {
|
||||
|
||||
// which response code is actually irrelevant here and was picked at random
|
||||
// OK code
|
||||
auto tmp = din_responseCodeType_OK;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 2;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_OK_bad_path_1) {
|
||||
|
||||
// OK code
|
||||
auto tmp = din_responseCodeType_OK;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = true; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 2;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_NE(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_OK_bad_path_2) {
|
||||
|
||||
// OK code
|
||||
auto tmp = din_responseCodeType_OK;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = true;
|
||||
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 2;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_NE(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_OK_bad_path_3) {
|
||||
|
||||
// OK code
|
||||
auto tmp = din_responseCodeType_OK;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_CERTIFICATE_INSTALLATION_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 2;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_NE(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_NO_EVENT_OK_bad_path_4) {
|
||||
|
||||
// OK code
|
||||
auto tmp = din_responseCodeType_OK;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = true;
|
||||
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 6;
|
||||
ctx->ev_v2g_data.received_session_id = 6;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_NE(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_NO_EVENT);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_SEND_AND_TERMINATE_1) {
|
||||
|
||||
auto tmp = din_responseCodeType_FAILED_WrongEnergyTransferType;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = true; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_METERING_RECEIPT_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 6;
|
||||
ctx->ev_v2g_data.received_session_id = 6;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_SEND_AND_TERMINATE);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_SEND_AND_TERMINATE_2) {
|
||||
|
||||
auto tmp = din_responseCodeType_FAILED_MeteringSignatureNotValid;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = true;
|
||||
|
||||
ctx->current_v2g_msg = V2G_CHARGING_STATUS_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 6;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_SEND_AND_TERMINATE);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_SEND_AND_TERMINATE_3) {
|
||||
|
||||
auto tmp = din_responseCodeType_OK_CertificateExpiresSoon;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_UNKNOWN_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 1;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_SEND_AND_TERMINATE);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_SEND_AND_TERMINATE_4) {
|
||||
|
||||
auto tmp = din_responseCodeType_OK_CertificateExpiresSoon;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_UNKNOWN_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 1;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_SEND_AND_TERMINATE);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_SEND_AND_TERMINATE_5) {
|
||||
|
||||
auto tmp = din_responseCodeType_OK_CertificateExpiresSoon;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_CABLE_CHECK_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 2;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_SEND_AND_TERMINATE);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_EVENT_SEND_AND_TERMINATE_6) {
|
||||
|
||||
auto tmp = din_responseCodeType_FAILED_SequenceError;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->stop_hlc = false; //||
|
||||
ctx->intl_emergency_shutdown = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_SESSION_SETUP_MSG; // &&
|
||||
ctx->evse_v2g_data.session_id = 1;
|
||||
ctx->ev_v2g_data.received_session_id = 2;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&tmp, conn.get()), V2G_EVENT_SEND_AND_TERMINATE);
|
||||
}
|
||||
TEST_F(DinServerTest, din_validate_response_code_V2G_DC_390) {
|
||||
// The response message shall contain the ResponseCode “FAILED_SequenceError” if the
|
||||
// SECC has received an unexpected request message.
|
||||
|
||||
auto given_response_code = din_responseCodeType_OK;
|
||||
constexpr auto expected_response_code = din_responseCodeType_FAILED_SequenceError;
|
||||
constexpr auto expected_response = V2G_EVENT_NO_EVENT;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = false;
|
||||
|
||||
ctx->current_v2g_msg = V2G_UNKNOWN_MSG;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&given_response_code, conn.get()), expected_response);
|
||||
// given response code should change in the function call
|
||||
EXPECT_EQ(given_response_code, expected_response_code);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_V2G_DC_391) {
|
||||
// The response message shall contain the ResponseCode “FAILED_UnknownSession” if the
|
||||
// SessionID in a request message does not match the SessionID provided by the SECC in the SessionSetupRes
|
||||
// message.
|
||||
|
||||
auto given_response_code = din_responseCodeType_OK;
|
||||
constexpr auto expected_response_code = din_responseCodeType_FAILED_UnknownSession;
|
||||
constexpr auto expected_response = V2G_EVENT_NO_EVENT;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = false;
|
||||
ctx->evse_v2g_data.session_id = 1234;
|
||||
ctx->ev_v2g_data.received_session_id = 5678;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&given_response_code, conn.get()), expected_response);
|
||||
// given response code should change in the function call
|
||||
EXPECT_EQ(given_response_code, expected_response_code);
|
||||
}
|
||||
|
||||
TEST_F(DinServerTest, din_validate_response_code_V2G_DC_665) {
|
||||
// If the SECC receives a request message that it expects according to the message sequence
|
||||
// specified in this chapter, and if the SECC cannot process this request message, e. g. due to
|
||||
// errors in the message parameters or due to impeding conditions in the EVSE, the SECC
|
||||
// shall:
|
||||
// [1.] without any delay, carry out an “EVSE-initiated emergency shutdown” as specified in
|
||||
// IEC 61851-23, which includes turning off the CP oscillator, if it is turned on,
|
||||
// [2.] respond with the corresponding response message with parameter ResponseCode
|
||||
// equal to “FAILED”, if possible, and
|
||||
// [3.] close the TCP connection according to [V2G-DC-116].
|
||||
|
||||
auto given_response_code = din_responseCodeType_FAILED;
|
||||
constexpr auto min_expected_response_code = din_responseCodeType_FAILED;
|
||||
constexpr auto expected_response = V2G_EVENT_SEND_AND_TERMINATE;
|
||||
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
ctx->terminate_connection_on_failed_response = true;
|
||||
|
||||
EXPECT_EQ(utils::din_validate_response_code(&given_response_code, conn.get()), expected_response);
|
||||
EXPECT_GE(given_response_code, min_expected_response_code);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,58 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef EVSE_SECURITYINTFSTUB_H_
|
||||
#define EVSE_SECURITYINTFSTUB_H_
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include "ModuleAdapterStub.hpp"
|
||||
#include "generated/types/evse_security.hpp"
|
||||
#include "utils/types.hpp"
|
||||
#include <functional>
|
||||
#include <generated/interfaces/evse_security/Interface.hpp>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace module::stub {
|
||||
|
||||
class evse_securityIntfStub : public evse_securityIntf {
|
||||
private:
|
||||
std::map<const std::string, Result (evse_securityIntfStub::*)(const Requirement& req, const Parameters& args)>
|
||||
functions;
|
||||
|
||||
public:
|
||||
evse_securityIntfStub(ModuleAdapterStub* adapter) :
|
||||
evse_securityIntf(adapter, Requirement{"", 0}, "EvseSecurity", std::nullopt) {
|
||||
functions["get_verify_file"] = &evse_securityIntfStub::get_verify_file;
|
||||
functions["get_leaf_certificate_info"] = &evse_securityIntfStub::get_leaf_certificate_info;
|
||||
}
|
||||
evse_securityIntfStub(ModuleAdapterStub& adapter) :
|
||||
evse_securityIntf(&adapter, Requirement{"", 0}, "EvseSecurity", std::nullopt) {
|
||||
functions["get_verify_file"] = &evse_securityIntfStub::get_verify_file;
|
||||
functions["get_leaf_certificate_info"] = &evse_securityIntfStub::get_leaf_certificate_info;
|
||||
}
|
||||
|
||||
virtual Result call_fn(const Requirement& req, const std::string& str, Parameters args) {
|
||||
if (auto it = functions.find(str); it != functions.end()) {
|
||||
return std::invoke(it->second, this, req, args);
|
||||
}
|
||||
std::printf("call_fn (%s)\n", str.c_str());
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
virtual Result get_verify_file(const Requirement& req, const Parameters& args) {
|
||||
std::cout << "evse_securityIntf::get_verify_file called" << std::endl;
|
||||
return "";
|
||||
}
|
||||
|
||||
virtual Result get_leaf_certificate_info(const Requirement& req, const Parameters& args) {
|
||||
std::cout << "evse_securityIntf::get_leaf_certificate_info called" << std::endl;
|
||||
return "";
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace module::stub
|
||||
|
||||
#endif // EVSE_SECURITYINTFSTUB_H_
|
||||
@@ -0,0 +1,30 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef ISO15118_EXTENTSIONSIMPLSTUB_H
|
||||
#define ISO15118_EXTENTSIONSIMPLSTUB_H
|
||||
|
||||
#include <iostream>
|
||||
|
||||
#include <generated/interfaces/iso15118_extensions/Implementation.hpp>
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
namespace module::stub {
|
||||
|
||||
class iso15118_extensionsImplStub : public iso15118_extensionsImplBase {
|
||||
public:
|
||||
iso15118_extensionsImplStub() : iso15118_extensionsImplBase(nullptr, "EvseV2G"){};
|
||||
|
||||
virtual void init() {
|
||||
}
|
||||
virtual void ready() {
|
||||
}
|
||||
|
||||
virtual void handle_set_get_certificate_response(types::iso15118::ResponseExiStreamStatus& certificate_response) {
|
||||
std::cout << "iso15118_extensionsImplBase::handle_set_get_certificate_response called" << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace module::stub
|
||||
|
||||
#endif // ISO15118_EXTENTSIONSIMPLSTUB_H
|
||||
@@ -0,0 +1,21 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#ifndef ISO15118_VASINTFSTUB_H_
|
||||
#define ISO15118_VASINTFSTUB_H_
|
||||
|
||||
#include "ModuleAdapterStub.hpp"
|
||||
#include <generated/interfaces/ISO15118_vas/Interface.hpp>
|
||||
|
||||
namespace module::stub {
|
||||
|
||||
class iso15118_vasIntfStub : public ISO15118_vasIntf {
|
||||
public:
|
||||
explicit iso15118_vasIntfStub(ModuleAdapterStub& adapter) :
|
||||
ISO15118_vasIntf(&adapter, Requirement{"", 0}, "EvseV2G", std::nullopt) {
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace module::stub
|
||||
|
||||
#endif // ISO15118_VASINTFSTUB_H_
|
||||
45
tools/EVerest-main/modules/EVSE/EvseV2G/tests/log.cpp
Normal file
45
tools/EVerest-main/modules/EVSE/EvseV2G/tests/log.cpp
Normal file
@@ -0,0 +1,45 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "utest_log.hpp"
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <map>
|
||||
|
||||
namespace {
|
||||
std::map<dloglevel_t, std::vector<std::string>> logged_events;
|
||||
|
||||
void add_log(dloglevel_t loglevel, const std::string& event) {
|
||||
logged_events[loglevel].push_back(event);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace module::stub {
|
||||
std::vector<std::string>& get_logs(dloglevel_t loglevel) {
|
||||
return logged_events[loglevel];
|
||||
}
|
||||
|
||||
void clear_logs() {
|
||||
logged_events.clear();
|
||||
}
|
||||
|
||||
} // namespace module::stub
|
||||
|
||||
void dlog_func(const dloglevel_t loglevel, const char* filename, const int linenumber, const char* functionname,
|
||||
const char* format, ...) {
|
||||
va_list ap;
|
||||
std::array<char, 256> buffer;
|
||||
va_start(ap, format);
|
||||
std::size_t len = std::vsnprintf(buffer.data(), buffer.size(), format, ap);
|
||||
va_end(ap);
|
||||
if (len > 0) {
|
||||
auto s_len = std::min(len, buffer.size());
|
||||
std::string event{buffer.data(), s_len};
|
||||
(void)std::fprintf(stderr, "log: %s\n", event.c_str());
|
||||
add_log(loglevel, event);
|
||||
}
|
||||
}
|
||||
165
tools/EVerest-main/modules/EVSE/EvseV2G/tests/openssl_test.cpp
Normal file
165
tools/EVerest-main/modules/EVSE/EvseV2G/tests/openssl_test.cpp
Normal file
@@ -0,0 +1,165 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include "crypto_common.hpp"
|
||||
#include "gtest/gtest.h"
|
||||
#include <crypto_openssl.hpp>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <everest/tls/openssl_util.hpp>
|
||||
#include <iso_server.hpp>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
#include <cbv2g/common/exi_bitstream.h>
|
||||
#include <cbv2g/exi_v2gtp.h> //for V2GTP_HEADER_LENGTHs
|
||||
#include <cbv2g/iso_2/iso2_msgDefDatatypes.h>
|
||||
#include <cbv2g/iso_2/iso2_msgDefDecoder.h>
|
||||
#include <cbv2g/iso_2/iso2_msgDefEncoder.h>
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T> constexpr void setCharacters(T& dest, const std::string& s) {
|
||||
dest.charactersLen = s.size();
|
||||
std::memcpy(&dest.characters[0], s.c_str(), s.size());
|
||||
}
|
||||
|
||||
template <typename T> constexpr void setBytes(T& dest, const std::uint8_t* b, std::size_t len) {
|
||||
dest.bytesLen = len;
|
||||
std::memcpy(&dest.bytes[0], b, len);
|
||||
}
|
||||
|
||||
struct test_vectors_t {
|
||||
const char* input;
|
||||
const std::uint8_t digest[32];
|
||||
};
|
||||
|
||||
#if 0
|
||||
// not used, useful to keep all the exi message test values together
|
||||
constexpr std::uint8_t sign_test[] = {0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c,
|
||||
0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55};
|
||||
|
||||
constexpr test_vectors_t sha_256_test[] = {
|
||||
{"", {0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f, 0xb9, 0x24,
|
||||
0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b, 0x78, 0x52, 0xb8, 0x55}},
|
||||
{"abc", {0xba, 0x78, 0x16, 0xbf, 0x8f, 0x01, 0xcf, 0xea, 0x41, 0x41, 0x40, 0xde, 0x5d, 0xae, 0x22, 0x23,
|
||||
0xb0, 0x03, 0x61, 0xa3, 0x96, 0x17, 0x7a, 0x9c, 0xb4, 0x10, 0xff, 0x61, 0xf2, 0x00, 0x15, 0xad}}};
|
||||
|
||||
// Test vectors from ISO 15118-2 Section J.2
|
||||
// checked okay (see iso_priv.pem)
|
||||
constexpr std::uint8_t iso_private_key[] = {0xb9, 0x13, 0x49, 0x63, 0xf5, 0x1c, 0x44, 0x14, 0x73, 0x84, 0x35,
|
||||
0x05, 0x7f, 0x97, 0xbb, 0xf1, 0x01, 0x0c, 0xab, 0xcb, 0x8d, 0xbd,
|
||||
0xe9, 0xc5, 0xd4, 0x81, 0x38, 0x39, 0x6a, 0xa9, 0x4b, 0x9d};
|
||||
// checked okay (see iso_priv.pem)
|
||||
constexpr std::uint8_t iso_public_key[] = {0x43, 0xe4, 0xfc, 0x4c, 0xcb, 0x64, 0x39, 0x04, 0x27, 0x9c, 0x7a, 0x5e, 0x65,
|
||||
0x76, 0xb3, 0x23, 0xe5, 0x5e, 0xc7, 0x9f, 0xf0, 0xe5, 0xa4, 0x05, 0x6e, 0x33,
|
||||
0x40, 0x84, 0xcb, 0xc3, 0x36, 0xff, 0x46, 0xe4, 0x4c, 0x1a, 0xdd, 0xf6, 0x91,
|
||||
0x62, 0xe5, 0x19, 0x2c, 0x2a, 0x83, 0xfc, 0x2b, 0xca, 0x9d, 0x8f, 0x46, 0xec,
|
||||
0xf4, 0xb7, 0x80, 0x67, 0xc2, 0x47, 0x6f, 0x6b, 0x3f, 0x34, 0x60, 0x0e};
|
||||
#endif
|
||||
|
||||
// EXI AuthorizationReq: checked okay (hash computes correctly)
|
||||
constexpr std::uint8_t iso_exi_a[] = {0x80, 0x04, 0x01, 0x52, 0x51, 0x0c, 0x40, 0x82, 0x9b, 0x7b, 0x6b, 0x29, 0x02,
|
||||
0x93, 0x0b, 0x73, 0x23, 0x7b, 0x69, 0x02, 0x23, 0x0b, 0xa3, 0x09, 0xe8};
|
||||
|
||||
// checked okay
|
||||
constexpr std::uint8_t iso_exi_a_hash[] = {0xd1, 0xb5, 0xe0, 0x3d, 0x00, 0x65, 0xbe, 0xe5, 0x6b, 0x31, 0x79,
|
||||
0x84, 0x45, 0x30, 0x51, 0xeb, 0x54, 0xca, 0x18, 0xfc, 0x0e, 0x09,
|
||||
0x16, 0x17, 0x4f, 0x8b, 0x3c, 0x77, 0xa9, 0x8f, 0x4a, 0xa9};
|
||||
|
||||
#if 0
|
||||
// not used, useful to keep all the exi message test values together
|
||||
// EXI AuthorizationReq signature block: checked okay (hash computes correctly)
|
||||
constexpr std::uint8_t iso_exi_b[] = {
|
||||
0x80, 0x81, 0x12, 0xb4, 0x3a, 0x3a, 0x38, 0x1d, 0x17, 0x97, 0xbb, 0xbb, 0xbb, 0x97, 0x3b, 0x99, 0x97, 0x37, 0xb9,
|
||||
0x33, 0x97, 0xaa, 0x29, 0x17, 0xb1, 0xb0, 0xb7, 0x37, 0xb7, 0x34, 0xb1, 0xb0, 0xb6, 0x16, 0xb2, 0xbc, 0x34, 0x97,
|
||||
0xa1, 0xab, 0x43, 0xa3, 0xa3, 0x81, 0xd1, 0x79, 0x7b, 0xbb, 0xbb, 0xb9, 0x73, 0xb9, 0x99, 0x73, 0x7b, 0x93, 0x39,
|
||||
0x79, 0x91, 0x81, 0x81, 0x89, 0x79, 0x81, 0xa1, 0x7b, 0xc3, 0x6b, 0x63, 0x23, 0x9b, 0x4b, 0x39, 0x6b, 0x6b, 0x7b,
|
||||
0x93, 0x29, 0x1b, 0x2b, 0x1b, 0x23, 0x9b, 0x09, 0x6b, 0x9b, 0x43, 0x09, 0x91, 0xa9, 0xb2, 0x20, 0x62, 0x34, 0x94,
|
||||
0x43, 0x10, 0x25, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, 0x77, 0x33, 0x2e, 0x6f, 0x72,
|
||||
0x67, 0x2f, 0x54, 0x52, 0x2f, 0x63, 0x61, 0x6e, 0x6f, 0x6e, 0x69, 0x63, 0x61, 0x6c, 0x2d, 0x65, 0x78, 0x69, 0x2f,
|
||||
0x48, 0x52, 0xd0, 0xe8, 0xe8, 0xe0, 0x74, 0x5e, 0x5e, 0xee, 0xee, 0xee, 0x5c, 0xee, 0x66, 0x5c, 0xde, 0xe4, 0xce,
|
||||
0x5e, 0x64, 0x60, 0x60, 0x62, 0x5e, 0x60, 0x68, 0x5e, 0xf0, 0xda, 0xd8, 0xca, 0xdc, 0xc6, 0x46, 0xe6, 0xd0, 0xc2,
|
||||
0x64, 0x6a, 0x6c, 0x84, 0x1a, 0x36, 0xbc, 0x07, 0xa0, 0x0c, 0xb7, 0xdc, 0xad, 0x66, 0x2f, 0x30, 0x88, 0xa6, 0x0a,
|
||||
0x3d, 0x6a, 0x99, 0x43, 0x1f, 0x81, 0xc1, 0x22, 0xc2, 0xe9, 0xf1, 0x67, 0x8e, 0xf5, 0x31, 0xe9, 0x55, 0x23, 0x70};
|
||||
#endif
|
||||
|
||||
// checked okay
|
||||
constexpr std::uint8_t iso_exi_b_hash[] = {0xa4, 0xe9, 0x03, 0xe1, 0x82, 0x43, 0x04, 0x1b, 0x55, 0x4e, 0x11,
|
||||
0x64, 0x7e, 0x10, 0x1e, 0xd2, 0x5f, 0xc9, 0xf2, 0x15, 0x2a, 0xf4,
|
||||
0x67, 0x40, 0x14, 0xfe, 0x2a, 0xde, 0xac, 0x1e, 0x1c, 0xf7};
|
||||
|
||||
// checked okay (verifies iso_exi_b_hash with iso_priv.pem)
|
||||
constexpr std::uint8_t iso_exi_sig[] = {0x4c, 0x8f, 0x20, 0xc1, 0x40, 0x0b, 0xa6, 0x76, 0x06, 0xaa, 0x48, 0x11, 0x57,
|
||||
0x2a, 0x2f, 0x1a, 0xd3, 0xc1, 0x50, 0x89, 0xd9, 0x54, 0x20, 0x36, 0x34, 0x30,
|
||||
0xbb, 0x26, 0xb4, 0x9d, 0xb1, 0x04, 0xf0, 0x8d, 0xfa, 0x8b, 0xf8, 0x05, 0x5e,
|
||||
0x63, 0xa4, 0xb7, 0x5a, 0x8d, 0x31, 0x69, 0x20, 0x6f, 0xa8, 0xd5, 0x43, 0x08,
|
||||
0xba, 0x58, 0xf0, 0x56, 0x6b, 0x96, 0xba, 0xf6, 0x92, 0xce, 0x59, 0x50};
|
||||
|
||||
#if 0
|
||||
// not used, useful to keep all the exi message test values together
|
||||
const char iso_exi_a_hash_b64[] = "0bXgPQBlvuVrMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=";
|
||||
const char iso_exi_a_hash_b64_nl[] = "0bXgPQBlvuVrMXmERTBR61TKGPwOCRYXT4s8d6mPSqk=\n";
|
||||
|
||||
const char iso_exi_sig_b64[] =
|
||||
"TI8gwUALpnYGqkgRVyovGtPBUInZVCA2NDC7JrSdsQTwjfqL+AVeY6S3Wo0xaSBvqNVDCLpY8FZrlrr2ks5ZUA==";
|
||||
const char iso_exi_sig_b64_nl[] =
|
||||
"TI8gwUALpnYGqkgRVyovGtPBUInZVCA2NDC7JrSdsQTwjfqL+AVeY6S3Wo0xaSBv\nqNVDCLpY8FZrlrr2ks5ZUA==\n";
|
||||
#endif
|
||||
|
||||
TEST(openssl, verifyIso) {
|
||||
auto* bio = BIO_new_file("iso_priv.pem", "r");
|
||||
ASSERT_NE(bio, nullptr);
|
||||
auto* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
|
||||
ASSERT_NE(pkey, nullptr);
|
||||
BIO_free(bio);
|
||||
|
||||
auto sig = openssl::bn_to_signature(&iso_exi_sig[0], &iso_exi_sig[32]);
|
||||
EXPECT_TRUE(openssl::verify(pkey, sig.get(), sig.size(), &iso_exi_b_hash[0], sizeof(iso_exi_b_hash)));
|
||||
EVP_PKEY_free(pkey);
|
||||
}
|
||||
|
||||
TEST(isoExi, signature) {
|
||||
// The message is:
|
||||
// header { SessionID, Signature}
|
||||
// body { AuthorizationReq }
|
||||
// the test vector doesn't include the entire encoded message
|
||||
|
||||
auto* bio = BIO_new_file("iso_priv.pem", "r");
|
||||
ASSERT_NE(bio, nullptr);
|
||||
auto* pkey = PEM_read_bio_PrivateKey(bio, nullptr, nullptr, nullptr);
|
||||
ASSERT_NE(pkey, nullptr);
|
||||
BIO_free(bio);
|
||||
|
||||
// decode the test vector AuthorizationReq
|
||||
struct iso2_exiFragment exi_a {};
|
||||
init_iso2_exiFragment(&exi_a);
|
||||
init_iso2_AuthorizationReqType(&exi_a.AuthorizationReq);
|
||||
|
||||
exi_bitstream_t stream;
|
||||
exi_bitstream_init(&stream, const_cast<std::uint8_t*>(&iso_exi_a[0]), sizeof(iso_exi_a), 0, nullptr);
|
||||
EXPECT_EQ(decode_iso2_exiFragment(&stream, &exi_a), 0);
|
||||
|
||||
// manually populate the Signature structure
|
||||
struct iso2_SignatureType sig {};
|
||||
init_iso2_SignatureType(&sig);
|
||||
|
||||
// SignedInfo
|
||||
setCharacters(sig.SignedInfo.CanonicalizationMethod.Algorithm, "http://www.w3.org/TR/canonical-exi/");
|
||||
setCharacters(sig.SignedInfo.SignatureMethod.Algorithm, "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256");
|
||||
sig.SignedInfo.Reference.arrayLen = 1;
|
||||
sig.SignedInfo.Reference.array[0].URI_isUsed = 1;
|
||||
setCharacters(sig.SignedInfo.Reference.array[0].URI, "#ID1");
|
||||
sig.SignedInfo.Reference.array[0].Transforms_isUsed = 1;
|
||||
setCharacters(sig.SignedInfo.Reference.array[0].Transforms.Transform.Algorithm,
|
||||
"http://www.w3.org/TR/canonical-exi/");
|
||||
setCharacters(sig.SignedInfo.Reference.array[0].DigestMethod.Algorithm, "http://www.w3.org/2001/04/xmlenc#sha256");
|
||||
setBytes(sig.SignedInfo.Reference.array[0].DigestValue, &iso_exi_a_hash[0], ::openssl::sha_256_digest_size);
|
||||
// SignatureValue
|
||||
setBytes(sig.SignatureValue.CONTENT, &iso_exi_sig[0], ::openssl::signature_size);
|
||||
EXPECT_TRUE(crypto::openssl::check_iso2_signature(&sig, pkey, &exi_a));
|
||||
|
||||
EVP_PKEY_free(pkey);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -0,0 +1,10 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "utils/types.hpp"
|
||||
|
||||
bool operator<(const Requirement& lhs, const Requirement& rhs) {
|
||||
return true;
|
||||
}
|
||||
104
tools/EVerest-main/modules/EVSE/EvseV2G/tests/sdp_test.cpp
Normal file
104
tools/EVerest-main/modules/EVSE/EvseV2G/tests/sdp_test.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include <gtest/gtest.h>
|
||||
#include <sdp.hpp>
|
||||
#include <tools.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
class SdpTest : public testing::Test {
|
||||
protected:
|
||||
SdpTest() {
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(SdpTest, sdp_set_dlink_ready_sets_flag) {
|
||||
v2g_context ctx{};
|
||||
EXPECT_FALSE(ctx.sdp_dlink_ready);
|
||||
|
||||
sdp_set_dlink_ready(&ctx, true);
|
||||
EXPECT_TRUE(ctx.sdp_dlink_ready);
|
||||
|
||||
sdp_set_dlink_ready(&ctx, false);
|
||||
EXPECT_FALSE(ctx.sdp_dlink_ready);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, sdp_set_dlink_ready_captures_timestamp) {
|
||||
v2g_context ctx{};
|
||||
EXPECT_EQ(ctx.sdp_dlink_ready_time.load(), 0);
|
||||
|
||||
sdp_set_dlink_ready(&ctx, true);
|
||||
EXPECT_GT(ctx.sdp_dlink_ready_time.load(), 0);
|
||||
|
||||
sdp_set_dlink_ready(&ctx, false);
|
||||
EXPECT_EQ(ctx.sdp_dlink_ready_time.load(), 0);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, sdp_set_dlink_ready_timestamp_is_monotonic) {
|
||||
v2g_context ctx{};
|
||||
|
||||
sdp_set_dlink_ready(&ctx, true);
|
||||
long long int first = ctx.sdp_dlink_ready_time.load();
|
||||
|
||||
sdp_set_dlink_ready(&ctx, false);
|
||||
sdp_set_dlink_ready(&ctx, true);
|
||||
long long int second = ctx.sdp_dlink_ready_time.load();
|
||||
|
||||
EXPECT_GE(second, first);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, sdp_write_header) {
|
||||
uint8_t buffer[20];
|
||||
uint16_t payload_type = 0x9001;
|
||||
uint32_t length = 367;
|
||||
|
||||
EXPECT_EQ(sdp_write_header(buffer, payload_type, length), 8);
|
||||
|
||||
EXPECT_EQ(buffer[0], 0x01);
|
||||
EXPECT_EQ(buffer[1], 0xFE);
|
||||
EXPECT_EQ(buffer[2], 0x90);
|
||||
EXPECT_EQ(buffer[3], 0x01);
|
||||
EXPECT_EQ(buffer[4], 0x00);
|
||||
EXPECT_EQ(buffer[5], 0x00);
|
||||
EXPECT_EQ(buffer[6], 0x01);
|
||||
EXPECT_EQ(buffer[7], 0x6F);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, timeout_detected_when_elapsed_exceeds_limit) {
|
||||
v2g_context ctx{};
|
||||
// Simulate dlink becoming ready 18001ms ago
|
||||
long long int now = getmonotonictime();
|
||||
ctx.sdp_dlink_ready_time = now - (V2G_COMMUNICATION_SETUP_TIMEOUT + 1);
|
||||
|
||||
long long int elapsed = now - ctx.sdp_dlink_ready_time.load();
|
||||
EXPECT_GE(elapsed, V2G_COMMUNICATION_SETUP_TIMEOUT);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, timeout_not_detected_when_elapsed_below_limit) {
|
||||
v2g_context ctx{};
|
||||
// Simulate dlink becoming ready just now
|
||||
ctx.sdp_dlink_ready_time = getmonotonictime();
|
||||
|
||||
long long int elapsed = getmonotonictime() - ctx.sdp_dlink_ready_time.load();
|
||||
EXPECT_LT(elapsed, V2G_COMMUNICATION_SETUP_TIMEOUT);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, timeout_cancelled_when_connection_initiated) {
|
||||
v2g_context ctx{};
|
||||
sdp_set_dlink_ready(&ctx, true);
|
||||
EXPECT_NE(ctx.sdp_dlink_ready_time.load(), 0);
|
||||
|
||||
// Simulate connection established — timeout should be cancelled
|
||||
ctx.connection_initiated = true;
|
||||
long long int dlink_ready_time = ctx.sdp_dlink_ready_time.load();
|
||||
if (ctx.connection_initiated && dlink_ready_time != 0) {
|
||||
ctx.sdp_dlink_ready_time = 0;
|
||||
}
|
||||
EXPECT_EQ(ctx.sdp_dlink_ready_time.load(), 0);
|
||||
}
|
||||
|
||||
TEST_F(SdpTest, v2g_communication_setup_timeout_is_18000) {
|
||||
EXPECT_EQ(V2G_COMMUNICATION_SETUP_TIMEOUT, 18000);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
36
tools/EVerest-main/modules/EVSE/EvseV2G/tests/tools_test.cpp
Normal file
36
tools/EVerest-main/modules/EVSE/EvseV2G/tests/tools_test.cpp
Normal file
@@ -0,0 +1,36 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include <gtest/gtest.h>
|
||||
#include <tools.hpp>
|
||||
|
||||
namespace {
|
||||
|
||||
template <typename T, std::size_t S> constexpr auto macStr(const T (&arg)[S]) {
|
||||
return to_mac_address_str(reinterpret_cast<const uint8_t*>(arg), S - 1);
|
||||
}
|
||||
|
||||
TEST(to_mac_address_str, various) {
|
||||
auto result = to_mac_address_str(nullptr, 0);
|
||||
EXPECT_TRUE(result.empty());
|
||||
const char* txt = "123";
|
||||
result = to_mac_address_str(reinterpret_cast<const uint8_t*>(txt), 0);
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = macStr("");
|
||||
EXPECT_TRUE(result.empty());
|
||||
result = macStr("12345678901234567"); // too long
|
||||
EXPECT_TRUE(result.empty());
|
||||
|
||||
result = macStr("A");
|
||||
EXPECT_EQ(result, "41");
|
||||
result = macStr("AB");
|
||||
EXPECT_EQ(result, "41:42");
|
||||
result = macStr("ABM");
|
||||
EXPECT_EQ(result, "41:42:4D");
|
||||
result = macStr("\xac\x91\xa1\x56\x5f\x46");
|
||||
EXPECT_EQ(result, "AC:91:A1:56:5F:46");
|
||||
result = macStr("1234567890123456"); // max length
|
||||
EXPECT_EQ(result, "31:32:33:34:35:36:37:38:39:30:31:32:33:34:35:36");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
15
tools/EVerest-main/modules/EVSE/EvseV2G/tests/utest_log.hpp
Normal file
15
tools/EVerest-main/modules/EVSE/EvseV2G/tests/utest_log.hpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <log.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace module::stub {
|
||||
std::vector<std::string>& get_logs(dloglevel_t loglevel);
|
||||
void clear_logs();
|
||||
|
||||
} // namespace module::stub
|
||||
162
tools/EVerest-main/modules/EVSE/EvseV2G/tests/v2g_ctx_test.cpp
Normal file
162
tools/EVerest-main/modules/EVSE/EvseV2G/tests/v2g_ctx_test.cpp
Normal file
@@ -0,0 +1,162 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright Pionix GmbH and Contributors to EVerest
|
||||
#include <memory>
|
||||
#include <v2g_ctx.hpp>
|
||||
|
||||
#include "ISO15118_chargerImplStub.hpp"
|
||||
#include "ModuleAdapterStub.hpp"
|
||||
#include "evse_securityIntfStub.hpp"
|
||||
#include "iso15118_extensionsImplStub.hpp"
|
||||
#include "iso15118_vasIntfStub.hpp"
|
||||
#include "utest_log.hpp"
|
||||
#include "v2g.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
namespace {
|
||||
|
||||
struct v2g_contextDeleter {
|
||||
void operator()(v2g_context* ptr) const {
|
||||
v2g_ctx_free(ptr);
|
||||
};
|
||||
};
|
||||
|
||||
class V2gCtxTest : public testing::Test {
|
||||
protected:
|
||||
std::unique_ptr<v2g_context, v2g_contextDeleter> ctx;
|
||||
module::stub::QuietModuleAdapterStub adapter;
|
||||
module::stub::ISO15118_chargerImplStub charger;
|
||||
module::stub::evse_securityIntfStub security;
|
||||
module::stub::iso15118_extensionsImplStub extensions;
|
||||
module::stub::iso15118_vasIntfStub vas_item;
|
||||
|
||||
V2gCtxTest() : charger(adapter), security(adapter), vas_item(adapter) {
|
||||
}
|
||||
|
||||
void v2g_ctx_init_charging_state_cleared() {
|
||||
// checks try to match the order is v2g.hpp
|
||||
|
||||
EXPECT_EQ(ctx->last_v2g_msg, V2G_UNKNOWN_MSG);
|
||||
EXPECT_EQ(ctx->current_v2g_msg, V2G_UNKNOWN_MSG);
|
||||
EXPECT_EQ(ctx->state, 0);
|
||||
|
||||
// not changed
|
||||
// is_dc_charger
|
||||
// debugMode
|
||||
// supported_protocols
|
||||
|
||||
EXPECT_EQ(ctx->selected_protocol, V2G_UNKNOWN_PROTOCOL);
|
||||
EXPECT_FALSE(ctx->intl_emergency_shutdown);
|
||||
EXPECT_FALSE(ctx->stop_hlc);
|
||||
|
||||
// ctx->is_connection_terminated is updated rather than cleared
|
||||
|
||||
// not changed
|
||||
// terminate_connection_on_failed_response
|
||||
// contactor_is_closed
|
||||
|
||||
// many items in session not reset
|
||||
EXPECT_FALSE(ctx->session.renegotiation_required);
|
||||
EXPECT_FALSE(ctx->session.is_charging);
|
||||
}
|
||||
|
||||
void SetUp() override {
|
||||
auto ptr = v2g_ctx_create(&charger, &extensions, &security, {&vas_item});
|
||||
ctx = std::unique_ptr<v2g_context, v2g_contextDeleter>(ptr, v2g_contextDeleter());
|
||||
module::stub::clear_logs();
|
||||
}
|
||||
|
||||
void TearDown() override {
|
||||
}
|
||||
};
|
||||
|
||||
TEST(RunFirst, v2g_ctx_init_charging_values) {
|
||||
// must not be part of V2gCtxTest
|
||||
// V2gCtxTest::SetUp() creates the v2g_context which would be the 1st
|
||||
// call to v2g_ctx_init_charging_values()
|
||||
|
||||
// only called from v2g_ctx_init_charging_session()
|
||||
// which is called from v2g_ctx_create()
|
||||
|
||||
// note v2g_ctx_init_charging_values() has a static bool so it
|
||||
// performs different tidyup after the first time it is called
|
||||
|
||||
v2g_context ctx;
|
||||
ctx.evse_v2g_data.charge_service.FreeService = 9;
|
||||
v2g_ctx_init_charging_values(&ctx);
|
||||
EXPECT_EQ(ctx.evse_v2g_data.charge_service.FreeService, 0);
|
||||
ctx.evse_v2g_data.charge_service.FreeService = 10;
|
||||
v2g_ctx_init_charging_values(&ctx);
|
||||
EXPECT_EQ(ctx.evse_v2g_data.charge_service.FreeService, 10);
|
||||
|
||||
// reset back to a valid value as it will never be reset
|
||||
ctx.evse_v2g_data.charge_service.FreeService = 0;
|
||||
}
|
||||
|
||||
TEST_F(V2gCtxTest, v2g_ctx_init_charging_stateTrue) {
|
||||
// called on session start in v2g_handle_connection()
|
||||
|
||||
ctx->last_v2g_msg = V2G_CABLE_CHECK_MSG;
|
||||
ctx->current_v2g_msg = V2G_CHARGE_PARAMETER_DISCOVERY_MSG;
|
||||
ctx->state = 10;
|
||||
ctx->selected_protocol = V2G_PROTO_DIN70121;
|
||||
ctx->intl_emergency_shutdown = true;
|
||||
ctx->stop_hlc = true;
|
||||
ctx->session.renegotiation_required = true;
|
||||
ctx->session.is_charging = true;
|
||||
|
||||
v2g_ctx_init_charging_state(ctx.get(), true);
|
||||
|
||||
v2g_ctx_init_charging_state_cleared();
|
||||
EXPECT_TRUE(ctx->is_connection_terminated);
|
||||
}
|
||||
|
||||
TEST_F(V2gCtxTest, v2g_ctx_init_charging_stateFalse) {
|
||||
// called on session end in v2g_handle_connection()
|
||||
|
||||
ctx->last_v2g_msg = V2G_CABLE_CHECK_MSG;
|
||||
ctx->current_v2g_msg = V2G_CHARGE_PARAMETER_DISCOVERY_MSG;
|
||||
ctx->state = 10;
|
||||
ctx->selected_protocol = V2G_PROTO_DIN70121;
|
||||
ctx->intl_emergency_shutdown = true;
|
||||
ctx->stop_hlc = true;
|
||||
ctx->session.renegotiation_required = true;
|
||||
ctx->session.is_charging = true;
|
||||
|
||||
v2g_ctx_init_charging_state(ctx.get(), false);
|
||||
|
||||
v2g_ctx_init_charging_state_cleared();
|
||||
EXPECT_FALSE(ctx->is_connection_terminated);
|
||||
}
|
||||
|
||||
#if 0
|
||||
// v2g_ctx_init_charging_session() is a trivial implementation
|
||||
TEST_F(V2gCtxTest, v2g_ctx_init_charging_sessionTrue) {
|
||||
// called in connection_teardown()
|
||||
// calls v2g_ctx_init_charging_state
|
||||
// calls v2g_ctx_init_charging_values
|
||||
}
|
||||
|
||||
TEST_F(V2gCtxTest, v2g_ctx_init_charging_sessionFalse) {
|
||||
// called in connection_teardown()
|
||||
// calls v2g_ctx_init_charging_state
|
||||
// calls v2g_ctx_init_charging_values
|
||||
}
|
||||
#endif
|
||||
|
||||
TEST(valgrind, memcheck) {
|
||||
// v2g_ctx_free() now sets shutdown and joins the event thread,
|
||||
// so there is no use-after-free during cleanup.
|
||||
|
||||
// run via valgrind to ensure that malloc/free are working
|
||||
module::stub::QuietModuleAdapterStub adapter;
|
||||
module::stub::ISO15118_chargerImplStub charger(adapter);
|
||||
module::stub::evse_securityIntfStub security(adapter);
|
||||
module::stub::iso15118_extensionsImplStub extensions;
|
||||
module::stub::iso15118_vasIntfStub vas_item(adapter);
|
||||
|
||||
auto ptr = v2g_ctx_create(&charger, &extensions, &security, {&vas_item});
|
||||
v2g_ctx_free(ptr);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
175
tools/EVerest-main/modules/EVSE/EvseV2G/tests/v2g_main.cpp
Normal file
175
tools/EVerest-main/modules/EVSE/EvseV2G/tests/v2g_main.cpp
Normal file
@@ -0,0 +1,175 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// Copyright 2020 - 2023 Pionix GmbH and Contributors to EVerest
|
||||
|
||||
/*
|
||||
* testing options
|
||||
* openssl s_client -connect [fe80::ae91:a1ff:fec9:a947%3]:64109 -verify 2 -CAfile server_root_cert.pem -cert
|
||||
* client_cert.pem -cert_chain client_chain.pem -key client_priv.pem -verify_return_error -verify_hostname
|
||||
* evse.pionix.de -status
|
||||
*/
|
||||
|
||||
#include <array>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <cstdlib>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <thread>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "ISO15118_chargerImplStub.hpp"
|
||||
#include "ModuleAdapterStub.hpp"
|
||||
#include "evse_securityIntfStub.hpp"
|
||||
#include "iso15118_extensionsImplStub.hpp"
|
||||
#include "iso15118_vasIntfStub.hpp"
|
||||
|
||||
#include <connection.hpp>
|
||||
#include <everest/tls/tls.hpp>
|
||||
#include <v2g_ctx.hpp>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
// needs to be in the global namespace
|
||||
int v2g_handle_connection(struct v2g_connection* conn) {
|
||||
assert(conn != nullptr);
|
||||
assert(conn->read != nullptr);
|
||||
assert(conn->write != nullptr);
|
||||
|
||||
std::array<unsigned char, 1024> buffer{};
|
||||
bool bExit = false;
|
||||
while (!bExit) {
|
||||
const ssize_t readbytes = conn->read(conn, buffer.data(), buffer.size());
|
||||
if (readbytes > 0) {
|
||||
const ssize_t writebytes = conn->write(conn, buffer.data(), readbytes);
|
||||
if (writebytes <= 0) {
|
||||
bExit = true;
|
||||
}
|
||||
} else if (readbytes < 0) {
|
||||
bExit = true;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
const char* interface;
|
||||
|
||||
void parse_options(int argc, char** argv) {
|
||||
interface = nullptr;
|
||||
int c;
|
||||
|
||||
while ((c = getopt(argc, argv, "hi:")) != -1) {
|
||||
switch (c) {
|
||||
case 'i':
|
||||
interface = optarg;
|
||||
break;
|
||||
case 'h':
|
||||
case '?':
|
||||
std::cout << "Usage: " << argv[0] << " -i <interface name>" << std::endl;
|
||||
exit(1);
|
||||
break;
|
||||
default:
|
||||
exit(2);
|
||||
}
|
||||
}
|
||||
|
||||
if (interface == nullptr) {
|
||||
std::cerr << "Error: " << argv[0] << " requires -i <interface name>" << std::endl;
|
||||
exit(3);
|
||||
}
|
||||
}
|
||||
|
||||
// EvseSecurity "implementation"
|
||||
struct EvseSecurity : public module::stub::evse_securityIntfStub {
|
||||
EvseSecurity(module::stub::ModuleAdapterStub& adapter) : module::stub::evse_securityIntfStub(&adapter) {
|
||||
}
|
||||
|
||||
Result get_verify_file(const Requirement& req, const Parameters& args) override {
|
||||
return "client_root_cert.pem";
|
||||
}
|
||||
|
||||
virtual Result get_leaf_certificate_info(const Requirement& req, const Parameters& args) override {
|
||||
// using types::evse_security::CertificateHashDataType;
|
||||
using types::evse_security::CertificateInfo;
|
||||
using types::evse_security::CertificateOCSP;
|
||||
using types::evse_security::GetCertificateInfoResult;
|
||||
using types::evse_security::GetCertificateInfoStatus;
|
||||
using types::evse_security::HashAlgorithm;
|
||||
|
||||
CertificateInfo cert_info;
|
||||
cert_info.key = "server_priv.pem";
|
||||
cert_info.certificate = "server_chain.pem";
|
||||
cert_info.certificate_count = 2;
|
||||
cert_info.ocsp = {{
|
||||
{HashAlgorithm::SHA256},
|
||||
{"ocsp_response.der"},
|
||||
},
|
||||
{
|
||||
{HashAlgorithm::SHA256},
|
||||
{"ocsp_response.der"},
|
||||
}};
|
||||
|
||||
const GetCertificateInfoResult res = {
|
||||
GetCertificateInfoStatus::Accepted,
|
||||
cert_info,
|
||||
};
|
||||
json jres = res;
|
||||
return jres;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
parse_options(argc, argv);
|
||||
|
||||
tls::Server tls_server;
|
||||
module::stub::ModuleAdapterStub adapter;
|
||||
module::stub::ISO15118_chargerImplStub charger(adapter);
|
||||
EvseSecurity security(adapter);
|
||||
module::stub::iso15118_extensionsImplStub extensions;
|
||||
module::stub::iso15118_vasIntfStub vas_item(adapter);
|
||||
|
||||
auto* ctx = v2g_ctx_create(&charger, &extensions, &security, {&vas_item});
|
||||
|
||||
if (ctx == nullptr) {
|
||||
std::cerr << "failed to create context" << std::endl;
|
||||
} else {
|
||||
ctx->tls_server = &tls_server;
|
||||
ctx->if_name = interface;
|
||||
ctx->tls_security = TLS_SECURITY_FORCE;
|
||||
ctx->is_connection_terminated = false;
|
||||
|
||||
std::thread stop([ctx]() {
|
||||
// there is a 60 second read timeout in connection.cpp
|
||||
std::this_thread::sleep_for(75s);
|
||||
std::cout << "shutdown" << std::endl;
|
||||
ctx->is_connection_terminated = true;
|
||||
ctx->shutdown = true;
|
||||
});
|
||||
|
||||
std::cout << "connection_init" << std::endl;
|
||||
if (::connection_init(ctx) != 0) {
|
||||
std::cerr << "connection_init failed" << std::endl;
|
||||
} else {
|
||||
std::cout << "connection_init started" << std::endl;
|
||||
}
|
||||
|
||||
std::cout << "connection_start_servers " << std::endl;
|
||||
if (::connection_start_servers(ctx) != 0) {
|
||||
std::cerr << "connection_start_servers failed" << std::endl;
|
||||
} else {
|
||||
std::cout << "connection_start_servers started" << std::endl;
|
||||
}
|
||||
|
||||
stop.join();
|
||||
tls::ServerConnection::wait_all_closed();
|
||||
|
||||
// wait for v2g_ctx_start_events thread to stop
|
||||
std::this_thread::sleep_for(2s);
|
||||
v2g_ctx_free(ctx);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user