Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ schemas
venv
*~
test/*
tests/build/
46 changes: 46 additions & 0 deletions tests/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
cmake_minimum_required(VERSION 3.14)

project(cbexigen VERSION 1)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)

find_package(GTest)

add_subdirectory(cbv2g)

file(GLOB_RECURSE CBV2G_TEST_SOURCES
./unit/sap/*.cpp
./unit/iso20/common/*.cpp
./unit/iso2/*.cpp
./unit/xmldsig/*.cpp
)

add_executable(test-${PROJECT_NAME}
${CBV2G_TEST_SOURCES}
)

target_compile_options(test-${PROJECT_NAME} PRIVATE -Wall -Werror -Wshadow)

target_include_directories(test-${PROJECT_NAME}
PRIVATE
${CMAKE_CURRENT_SOURCE_DIR}
)

target_link_libraries(test-${PROJECT_NAME}
PRIVATE
gtest
gtest_main
gmock
pthread
cbv2g
)

include(GoogleTest)
gtest_discover_tests(test-${PROJECT_NAME}
TEST_PREFIX ${PROJECT_NAME}
TEST_LIST ${PROJECT_NAME}Tests
)

set_tests_properties(${${PROJECT_NAME}Tests} PROPERTIES TIMEOUT 10)
32 changes: 32 additions & 0 deletions tests/cbv2g/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
cmake_minimum_required(VERSION 3.18)

set(CMAKE_C_STANDARD 11)
set(CMAKE_C_STANDARD_REQUIRED ON)

set(CBV2G_SOURCE_PATH "../../src/output/c")

file(GLOB_RECURSE CBV2G_SOURCES
${CBV2G_SOURCE_PATH}/common/*.c
${CBV2G_SOURCE_PATH}/appHandshake/*.c
${CBV2G_SOURCE_PATH}/din/*.c
${CBV2G_SOURCE_PATH}/iso-2/*.c
${CBV2G_SOURCE_PATH}/iso-20/*.c
${CBV2G_SOURCE_PATH}/v2gtp/*.c
)

add_library(cbv2g STATIC
${CBV2G_SOURCES}
)

target_compile_options(cbv2g PRIVATE -Werror -Wall -Wextra)

target_include_directories(cbv2g
PUBLIC
${CBV2G_SOURCE_PATH}/
${CBV2G_SOURCE_PATH}/appHandshake
${CBV2G_SOURCE_PATH}/common
${CBV2G_SOURCE_PATH}/din
${CBV2G_SOURCE_PATH}/iso-2
${CBV2G_SOURCE_PATH}/iso-20
${CBV2G_SOURCE_PATH}/v2gtp
)
70 changes: 70 additions & 0 deletions tests/unit/iso20/common/test_sessionSetup.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <exi_v2gtp.h>
#include <iso20_CommonMessages_Decoder.h>
#include <iso20_CommonMessages_Encoder.h>

#include "../test_header_utils.hpp"

class Test_SessionSetup : public testing::Test {
protected:
uint8_t m_data[256] = {0};
exi_bitstream_t m_stream;
};

/** \param xml_input (that was used to generate the EXI binary using EXIficient)
* <?xml version="1.0" encoding="UTF-8"?>
* <ns0:SessionSetupReq xmlns:ns0="urn:iso:std:iso:15118:-20:CommonMessages">
* <ns1:Header xmlns:ns1="urn:iso:std:iso:15118:-20:CommonTypes">
* <ns1:SessionID>3030303030303030</ns1:SessionID>
* <ns1:TimeStamp>1707896956850052</ns1:TimeStamp>
* </ns1:Header>
* <ns0:EVCCID>PIXV12345678901231</ns0:EVCCID>
* </ns0:SessionSetupReq>
*/
TEST_F(Test_SessionSetup, WhenEncodingKnownSessionSetupRequest_ThenResultMatchesExpected) {
static constexpr uint8_t expected[] =
"\x01\xFE\x80\x02\x00\x00\x00\x28" //<- header
"\x80\x8c\x04\x18\x18\x18\x18\x18\x18\x18\x18\x08\x49\xfb\x4f\xba\xba\xa8\x40\x32\x0a\x28\x24\xac\x2b\x18\x99"
"\x19\x9a\x1a\x9b\x1b\x9c\x1c\x98\x18\x99\x19\x98\x80";
static constexpr size_t streamLen = 0x28;
exi_bitstream_init(&m_stream, m_data, sizeof(m_data), 8, NULL);
iso20_exiDocument exiDoc = {};
exiDoc.SessionSetupReq_isUsed = 1;
uint8_t sessionID[] = "\x30\x30\x30\x30\x30\x30\x30\x30";
setHeader(exiDoc.SessionSetupReq.Header, sessionID, 1707896956850052);
setString(exiDoc.SessionSetupReq.EVCCID, "PIXV12345678901231");

int res = encode_iso20_exiDocument(&m_stream, &exiDoc);
size_t len = exi_bitstream_get_length(&m_stream);
V2GTP20_WriteHeader(&m_data[0], len, V2GTP20_MAINSTREAM_PAYLOAD_ID);

ASSERT_EQ(res, 0);
ASSERT_EQ(len, streamLen);
ASSERT_EQ(memcmp(m_data, expected, sizeof(expected) - 1), 0)
<< std::string("\\x") << toHexStr(m_data, m_data + sizeof(expected) - 1, "\\x") << '\n'
<< std::string("\\x") << toHexStr(&expected[0], &expected[0] + sizeof(expected) - 1, "\\x");
}

TEST_F(Test_SessionSetup, WhenDecodingKnownSessionSetupRequest_ThenResultMatchesExpected) {
uint8_t input[] =
"\x01\xFE\x80\x02\x00\x00\x00\x28" //<- header
"\x80\x8c\x04\x18\x18\x18\x18\x18\x18\x18\x18\x08\x49\xfb\x4f\xba\xba\xa8\x40\x32\x0a\x28\x24\xac\x2b\x18\x99"
"\x19\x9a\x1a\x9b\x1b\x9c\x1c\x98\x18\x99\x19\x98\x80";
iso20_exiDocument exiDoc = {};
exi_bitstream_init(&m_stream, &input[0], sizeof(input), 8, NULL);
uint32_t len = 0;

int res = V2GTP20_ReadHeader(&input[0], &len, V2GTP20_MAINSTREAM_PAYLOAD_ID);
ASSERT_EQ(res, 0);
res = decode_iso20_exiDocument(&m_stream, &exiDoc);
ASSERT_EQ(res, 0);

static constexpr size_t streamLen = 0x28;
ASSERT_EQ(len, streamLen);
ASSERT_EQ((int)exiDoc.SessionSetupReq_isUsed, 1);
static const uint8_t sessionID[] = "\x30\x30\x30\x30\x30\x30\x30\x30";
ASSERT_ISO20_HEADER_EQ(exiDoc.SessionSetupReq.Header, sessionID, 1707896956850052);
ASSERT_ISO20_STREQ(exiDoc.SessionSetupReq.EVCCID, "PIXV12345678901231");
}
68 changes: 68 additions & 0 deletions tests/unit/iso20/test_header_utils.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#pragma once

#include <cstddef>
#include <cstdint>
#include <gtest/gtest.h>
#include <iomanip>
#include <optional>
#include <span>
#include <string>

template <typename HeaderT, size_t Len>
inline void setHeader(HeaderT& header, const uint8_t (&sessionID)[Len], uint64_t timeStamp,
const std::optional<decltype(header.Signature)>& signature = std::nullopt) {
static_assert(Len >= 8, "SessionID must be size 8. Larger is allowed for string literal \\0 term");
header.SessionID.bytesLen = 8;
std::copy(&sessionID[0], &sessionID[0] + 8, header.SessionID.bytes);
header.TimeStamp = timeStamp;
header.Signature_isUsed = false;
if (signature) {
throw std::runtime_error("not supported yet");
}
}
template <typename HeaderT>
inline bool assertHeader(const HeaderT& header, const uint8_t* sessionID, uint64_t timeStamp) {
bool success = false;
[&]() {
ASSERT_EQ(header.SessionID.bytesLen, 8);
ASSERT_EQ(memcmp(header.SessionID.bytes, sessionID, 8), 0);
ASSERT_EQ((int)header.Signature_isUsed, 0);
ASSERT_EQ(header.TimeStamp, timeStamp);
success = true;
}();
return success;
}
#define ASSERT_ISO20_HEADER_EQ(...) \
do { /* NOLINT */ \
if (!assertHeader(__VA_ARGS__)) { \
return; \
} \
} while (0) /* NOLINT */
#define ASSERT_ISO20_STREQ(str, sv) \
do { /* NOLINT */ \
std::string_view svv = sv; \
ASSERT_EQ((str).charactersLen, svv.size()); \
ASSERT_EQ((str).characters, svv); \
} while (0) /* NOLINT */

template <typename StrT>
inline void setString(StrT& out, std::string_view in) {
out.charactersLen = in.size();
std::copy(in.begin(), in.end(), out.characters);
}

template <typename StrT>
inline void setBytes(StrT& out, std::span<uint8_t> in) {
out.bytesLen = in.size();
std::copy(in.begin(), in.end(), out.bytes);
}

template <typename It>
std::string toHexStr(It begin, It end, std::string_view delimit = "\\x") {
std::stringstream ss;
auto it = begin;
for (; it != end; ++it) {
ss << delimit << std::setfill('0') << std::setw(2) << std::hex << (uint16_t)*it;
}
return std::move(ss).str();
}
174 changes: 174 additions & 0 deletions tests/unit/sap/test_supportedAppProtocol.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
#include <gmock/gmock.h>
#include <gtest/gtest.h>

#include <appHand_Decoder.h>
#include <appHand_Encoder.h>
#include <exi_v2gtp.h>

class Test_SupportedAppProtocol : public testing::Test {
protected:
uint8_t m_data[256] = {0};
exi_bitstream_t m_stream;
};

static void setProtocol(appHand_AppProtocolType& protoc, std::string_view ns, uint32_t vMaj, uint32_t vMin,
uint8_t schemaID, uint8_t priority) {
protoc.ProtocolNamespace.charactersLen = ns.size();
std::copy(ns.begin(), ns.end(), protoc.ProtocolNamespace.characters);
protoc.VersionNumberMajor = vMaj;
protoc.VersionNumberMinor = vMin;
protoc.SchemaID = schemaID;
protoc.Priority = priority;
}

/** \param xml input:
* <?xml version="1.0" encoding="UTF-8"?>
* <ns4:supportedAppProtocolReq xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xmlns:ns3="http://www.w3.org/2001/XMLSchema" xmlns:ns4="urn:iso:15118:2:2010:AppProtocol"> <AppProtocol>
* <ProtocolNamespace>urn:iso:15118:2:2010:MsgDef</ProtocolNamespace>
* <VersionNumberMajor>1</VersionNumberMajor>
* <VersionNumberMinor>0</VersionNumberMinor>
* <SchemaID>1</SchemaID>
* <Priority>1</Priority>
* </AppProtocol>
* <AppProtocol>
* <ProtocolNamespace>urn:din:70121:2012:MsgDef</ProtocolNamespace>
* <VersionNumberMajor>1</VersionNumberMajor>
* <VersionNumberMinor>0</VersionNumberMinor>
* <SchemaID>2</SchemaID>
* <Priority>2</Priority>
* </AppProtocol>
* <AppProtocol>
* <ProtocolNamespace>urn:iso:std:iso:15118:-20:DC</ProtocolNamespace>
* <VersionNumberMajor>1</VersionNumberMajor>
* <VersionNumberMinor>0</VersionNumberMinor>
* <SchemaID>3</SchemaID>
* <Priority>3</Priority>
* </AppProtocol>
* </ns4:supportedAppProtocolReq>
*/
TEST_F(Test_SupportedAppProtocol, WhenEncodingKnownSupportedAppProtocolRequest_ThenResultMatchesExpected) {
static constexpr uint8_t expected[] =
"\x01\xFE\x80\x01\x00\x00\x00\x66" //<-header
"\x80\x00\xeb\xab\x93\x71\xd3\x4b\x9b\x79\xd1\x89\xa9\x89\x89\xc1\xd1\x91\xd1\x91\x81\x89\x81\xd2\x6b\x9b\x3a"
"\x23\x2b\x30\x01\x00\x00\x04\x00\x01\xb7\x57\x26\xe3\xa6\x46\x96\xe3\xa3\x73\x03\x13\x23\x13\xa3\x23\x03\x13"
"\x23\xa4\xd7\x36\x74\x46\x56\x60\x02\x00\x00\x10\x08\x03\xce\xae\x4d\xc7\x4d\x2e\x6d\xe7\x4e\x6e\x8c\x87\x4d"
"\x2e\x6d\xe7\x46\x26\xa6\x26\x27\x07\x45\xa6\x46\x07\x48\x88\x60\x04\x00\x00\x30\x21";
static constexpr size_t streamLen = 0x66;
static constexpr std::string_view namespaceISO15118_2 = "urn:iso:15118:2:2010:MsgDef";
static constexpr std::string_view namespaceDin70121 = "urn:din:70121:2012:MsgDef";
static constexpr std::string_view namespaceISO15118_20 = "urn:iso:std:iso:15118:-20:DC";
exi_bitstream_init(&m_stream, m_data, sizeof(m_data), 8, NULL);
appHand_exiDocument exiDoc = {};
exiDoc.supportedAppProtocolReq_isUsed = 1;
exiDoc.supportedAppProtocolReq.AppProtocol.arrayLen = 3;
setProtocol(exiDoc.supportedAppProtocolReq.AppProtocol.array[0], namespaceISO15118_2, 1, 0, 1, 1);
setProtocol(exiDoc.supportedAppProtocolReq.AppProtocol.array[1], namespaceDin70121, 1, 0, 2, 2);
setProtocol(exiDoc.supportedAppProtocolReq.AppProtocol.array[2], namespaceISO15118_20, 1, 0, 3, 3);

int res = encode_appHand_exiDocument(&m_stream, &exiDoc);
int len = exi_bitstream_get_length(&m_stream);
V2GTP_WriteHeader(m_data, len);

ASSERT_EQ(res, 0);
ASSERT_EQ(len, streamLen);
ASSERT_EQ(memcmp(m_data, expected, sizeof(expected) - 1), 0);
}

TEST_F(Test_SupportedAppProtocol, WhenDecodingKnownSupportedAppProtocolRequestStream_ThenResultMatchesExpected) {
uint8_t input[] =
"\x01\xFE\x80\x01\x00\x00\x00\x66" //<-header
"\x80\x00\xeb\xab\x93\x71\xd3\x4b\x9b\x79\xd1\x89\xa9\x89\x89\xc1\xd1\x91\xd1\x91\x81\x89\x81\xd2\x6b\x9b\x3a"
"\x23\x2b\x30\x01\x00\x00\x04\x00\x01\xb7\x57\x26\xe3\xa6\x46\x96\xe3\xa3\x73\x03\x13\x23\x13\xa3\x23\x03\x13"
"\x23\xa4\xd7\x36\x74\x46\x56\x60\x02\x00\x00\x10\x08\x03\xce\xae\x4d\xc7\x4d\x2e\x6d\xe7\x4e\x6e\x8c\x87\x4d"
"\x2e\x6d\xe7\x46\x26\xa6\x26\x27\x07\x45\xa6\x46\x07\x48\x88\x60\x04\x00\x00\x30\x21";
exi_bitstream_init(&m_stream, input, sizeof(input), 8, NULL);
appHand_exiDocument exiDoc = {};
uint32_t len = 0;

int res = V2GTP_ReadHeader(input, &len);
ASSERT_EQ(res, 0);
res = decode_appHand_exiDocument(&m_stream, &exiDoc);
ASSERT_EQ(res, 0);

static constexpr size_t streamLen = 0x66;
static constexpr std::string_view namespaceISO15118_2 = "urn:iso:15118:2:2010:MsgDef";
static constexpr std::string_view namespaceDin70121 = "urn:din:70121:2012:MsgDef";
static constexpr std::string_view namespaceISO15118_20 = "urn:iso:std:iso:15118:-20:DC";
ASSERT_EQ(len, streamLen);
ASSERT_EQ((int)exiDoc.supportedAppProtocolReq_isUsed, 1);
ASSERT_EQ(exiDoc.supportedAppProtocolReq.AppProtocol.arrayLen, 3);

auto& protoc0 = exiDoc.supportedAppProtocolReq.AppProtocol.array[0];
ASSERT_EQ(protoc0.ProtocolNamespace.charactersLen, namespaceISO15118_2.size());
ASSERT_EQ(memcmp(protoc0.ProtocolNamespace.characters, namespaceISO15118_2.data(), namespaceISO15118_2.size()), 0);
ASSERT_EQ(protoc0.VersionNumberMajor, 1);
ASSERT_EQ(protoc0.VersionNumberMinor, 0);
ASSERT_EQ(protoc0.SchemaID, 1);
ASSERT_EQ(protoc0.Priority, 1);

auto& protoc1 = exiDoc.supportedAppProtocolReq.AppProtocol.array[1];
ASSERT_EQ(protoc1.ProtocolNamespace.charactersLen, namespaceDin70121.size());
ASSERT_EQ(memcmp(protoc1.ProtocolNamespace.characters, namespaceDin70121.data(), namespaceDin70121.size()), 0);
ASSERT_EQ(protoc1.VersionNumberMajor, 1);
ASSERT_EQ(protoc1.VersionNumberMinor, 0);
ASSERT_EQ(protoc1.SchemaID, 2);
ASSERT_EQ(protoc1.Priority, 2);

auto& protoc2 = exiDoc.supportedAppProtocolReq.AppProtocol.array[2];
ASSERT_EQ(protoc2.ProtocolNamespace.charactersLen, namespaceISO15118_20.size());
ASSERT_EQ(memcmp(protoc2.ProtocolNamespace.characters, namespaceISO15118_20.data(), namespaceISO15118_20.size()),
0);
ASSERT_EQ(protoc2.VersionNumberMajor, 1);
ASSERT_EQ(protoc2.VersionNumberMinor, 0);
ASSERT_EQ(protoc2.SchemaID, 3);
ASSERT_EQ(protoc2.Priority, 3);
}

/** \param xml input:
* <?xml version="1.0" encoding="UTF-8"?>
* <ns4:supportedAppProtocolRes
* xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
* xmlns:ns3="http://www.w3.org/2001/XMLSchema"
* xmlns:ns4="urn:iso:15118:2:2010:AppProtocol">
* <ResponseCode>OK_SuccessfulNegotiation</ResponseCode>
* <SchemaID>1</SchemaID>
* </ns4:supportedAppProtocolRes>
*/
TEST_F(Test_SupportedAppProtocol, WhenEncodingKnownSupportedAppProtocolResponse_ThenResultMatchesExpected) {
static constexpr uint8_t expected[] = "\x01\xfe\x80\x01\x00\x00\x00\x04" //<-header
"\x80\x40\x00\x40";

static constexpr size_t streamLen = 0x04;
exi_bitstream_init(&m_stream, m_data, sizeof(m_data), 8, NULL);
appHand_exiDocument exiDoc = {};
exiDoc.supportedAppProtocolRes_isUsed = 1;
exiDoc.supportedAppProtocolRes.SchemaID_isUsed = 1;
exiDoc.supportedAppProtocolRes.SchemaID = 1;
exiDoc.supportedAppProtocolRes.ResponseCode = appHand_responseCodeType_OK_SuccessfulNegotiation;

int res = encode_appHand_exiDocument(&m_stream, &exiDoc);
ASSERT_EQ(res, 0);
int len = exi_bitstream_get_length(&m_stream);
V2GTP_WriteHeader(m_data, len);

ASSERT_EQ(len, streamLen);
ASSERT_EQ(memcmp(m_data, expected, sizeof(expected) - 1), 0);
}
TEST_F(Test_SupportedAppProtocol, WhenDecodingKnownSupportedAppProtocolResponseStream_ThenResultMatchesExpected) {
uint8_t input[] = "\x01\xfe\x80\x01\x00\x00\x00\x04" //<-header
"\x80\x40\x00\x40";
exi_bitstream_init(&m_stream, input, sizeof(input), 8, NULL);
appHand_exiDocument exiDoc = {};
uint32_t len = 0;

V2GTP_ReadHeader(input, &len);
decode_appHand_exiDocument(&m_stream, &exiDoc);

static constexpr size_t streamLen = 0x04;
ASSERT_EQ(len, streamLen);
ASSERT_EQ((int)exiDoc.supportedAppProtocolRes_isUsed, 1);
ASSERT_EQ(exiDoc.supportedAppProtocolRes.ResponseCode, appHand_responseCodeType_OK_SuccessfulNegotiation);
ASSERT_EQ((int)exiDoc.supportedAppProtocolRes.SchemaID_isUsed, 1);
ASSERT_EQ(exiDoc.supportedAppProtocolRes.SchemaID, 1);
}
Loading