Skip to content

Commit e11f04d

Browse files
authored
Add testing support to the LLServer & Fix Sequencemanager bug (#7)
Adds testing support and refactors file I/O to enable unit tests for the LLServer’s SequenceManager, fixes the “start” bug, and ensures the “END” field is executed. - Introduce JSON test data and GoogleTest cases for SequenceManager behavior - Replace direct filesystem calls with a FileSystemAbstraction singleton for mocking - Update SequenceManager to use the new abstraction, add interpolation helpers, and correct timing
1 parent 9ae570a commit e11f04d

27 files changed

+948
-202
lines changed

.github/workflows/gtest.yml

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
name: Run GTests
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
jobs:
8+
build-and-test:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Checkout repository
13+
uses: actions/checkout@v4
14+
with:
15+
submodules: 'true'
16+
17+
- name: Install dependencies
18+
run: |
19+
sudo apt-get update
20+
sudo apt-get install -y cmake ninja-build g++
21+
22+
- name: Configure with CMake Preset
23+
run: cmake --preset=local-dev
24+
25+
- name: Build
26+
run: cmake --build build
27+
28+
- name: Run tests
29+
run: |
30+
cd build
31+
ctest --output-on-failure

CMakeLists.txt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,8 @@ set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall")
99

1010
include_directories(include include/can_houbolt)
1111

12-
file(GLOB_RECURSE sources main.cpp src/*.cpp src/*.c include/*.h)
12+
file(GLOB_RECURSE sources src/*.cpp src/*.c include/*.h)
13+
1314

1415
if(NO_PYTHON)
1516
MESSAGE(STATUS "Python features are not used")
@@ -20,10 +21,17 @@ else()
2021
include_directories(${Python3_INCLUDE_DIRS})
2122
endif()
2223

23-
add_executable(${PROJECT_NAME} ${sources})
24+
# Define a library for the main project
25+
add_library(${PROJECT_NAME}_lib STATIC ${sources})
2426

25-
option(TEST "test with simple CAN Driver simulation" OFF)
26-
if(TEST)
27+
# Create an executable that links to this library
28+
add_executable(${PROJECT_NAME} main.cpp)
29+
target_link_libraries(${PROJECT_NAME} PRIVATE ${PROJECT_NAME}_lib)
30+
option(E2E_TEST "test with simple CAN Driver simulation" OFF)
31+
option(NO_INFLUX "Disable influxdb logging" OFF)
32+
option(NO_CANLIB "Disable Kvaser CANLIB" OFF)
33+
option(NO_PYTHON "Disable Python" OFF)
34+
if(E2E_TEST)
2735
add_compile_definitions(TEST_LLSERVER)
2836
endif()
2937

@@ -42,7 +50,7 @@ if(UNIX AND NOT APPLE)
4250
endif()
4351

4452
if(LINUX)
45-
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads -lstdc++fs -latomic)
53+
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads -latomic)
4654

4755
if(NOT NO_CANLIB)
4856
target_link_libraries(${PROJECT_NAME} PRIVATE -lcanlib)
@@ -53,3 +61,8 @@ if(LINUX)
5361
else()
5462
target_link_libraries(${PROJECT_NAME} PRIVATE Threads::Threads)
5563
endif()
64+
65+
if(TEST)
66+
enable_testing()
67+
add_subdirectory(tests)
68+
endif ()

CMakePresets.json

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,19 @@
1818
"NO_INFLUX": "ON",
1919
"CMAKE_BUILD_TYPE": "Debug"
2020
}
21+
},
22+
{
23+
"name": "release",
24+
"displayName": "Release",
25+
"description": "Release build ",
26+
"generator": "Ninja",
27+
"binaryDir": "${sourceDir}/releaseBuild",
28+
"cacheVariables": {
29+
"NO_PYTHON": "OFF",
30+
"NO_CANLIB": "OFF",
31+
"NO_INFLUX": "OFF",
32+
"CMAKE_BUILD_TYPE": "Release"
33+
}
2134
}
2235
]
2336
}

include/EventManager.h

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -51,26 +51,28 @@ class EventManager : public Singleton<EventManager>
5151
double GetArgument(const std::string &stateName, nlohmann::json& param, double& newValue);
5252
void GetArgumentList(const std::string &stateName, nlohmann::json& event, std::vector<double>& argumentList, double& newValue);
5353

54-
~EventManager();
5554
public:
56-
57-
void Init(Config &config);
55+
virtual void Init(Config &config);
5856

5957
/**
6058
* this is used, so mapping and command loading don't need to be done at the same time,
6159
* this way, the event manager doesn't need to call any classes below the llinterface
6260
*/
63-
void Start();
61+
virtual void Start();
62+
63+
virtual void AddChannelTypes(std::map<std::string, std::string>& channelTypes);
64+
65+
virtual void AddCommands(std::map<std::string, command_t> commands);
66+
67+
virtual std::map<std::string, command_t> GetCommands();
6468

65-
void AddChannelTypes(std::map<std::string, std::string>& channelTypes);
66-
void AddCommands(std::map<std::string, command_t> commands);
67-
std::map<std::string, command_t> GetCommands();
68-
void OnStateChange(const std::string& stateName, double oldValue, double newValue);
69+
virtual void OnStateChange(const std::string& stateName, double oldValue, double newValue);
6970

70-
void ExecuteRegexCommandOrState(const std::string &regexKey, nlohmann::json &events, const std::string &stateName, double oldValue, double newValue, bool testOnly);
71-
void ExecuteCommandOrState(const std::string &stateName, double oldValue, double newValue, bool useDefaultMapping, bool testOnly);
72-
void ExecuteCommand(const std::string &commandName, std::vector<double> &params, bool testOnly);
71+
virtual void ExecuteRegexCommandOrState(const std::string &regexKey, nlohmann::json &events, const std::string &stateName, double oldValue, double newValue, bool testOnly);
72+
virtual void ExecuteCommandOrState(const std::string &stateName, double oldValue, double newValue, bool useDefaultMapping, bool testOnly);
73+
virtual void ExecuteCommand(const std::string &commandName, std::vector<double> &params, bool testOnly);
7374

75+
virtual ~EventManager();
7476

7577
};
7678
#endif //LLSERVER_ECUI_HOUBOLT_EVENTMANAGER_H

include/LLInterface.h

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -46,26 +46,26 @@ class LLInterface : public Singleton<LLInterface>
4646
static nlohmann::json StatesToJson(std::map<std::string, std::tuple<double, uint64_t>> &states);
4747
static nlohmann::json StatesToJson(std::map<std::string, std::tuple<double, uint64_t, bool>> &states);
4848

49-
~LLInterface();
49+
public:
50+
virtual ~LLInterface();
5051

51-
public:
52-
void Init(Config &config);
52+
virtual void Init(Config &config);
5353

54-
nlohmann::json GetGUIMapping();
54+
virtual nlohmann::json GetGUIMapping();
5555

56-
void TransmitStates(int64_t microTime, std::map<std::string, std::tuple<double, uint64_t>> &states);
56+
virtual void TransmitStates(int64_t microTime, std::map<std::string, std::tuple<double, uint64_t>> &states);
5757

58-
void StartStateTransmission(Config &config);
59-
void StopStateTransmission();
58+
virtual void StartStateTransmission(Config &config);
59+
virtual void StopStateTransmission();
6060

61-
nlohmann::json GetAllStates();
62-
nlohmann::json GetAllStateLabels();
61+
virtual nlohmann::json GetAllStates();
62+
virtual nlohmann::json GetAllStateLabels();
6363

64-
nlohmann::json GetStates(nlohmann::json &stateNames);
65-
void SetState(std::string stateName, double value, uint64_t timestamp);
64+
virtual nlohmann::json GetStates(nlohmann::json &stateNames);
65+
virtual void SetState(std::string stateName, double value, uint64_t timestamp);
6666

67-
void ExecuteCommand(std::string &commandName, std::vector<double> &params, bool testOnly);
68-
std::map<std::string, command_t> GetCommands();
67+
virtual void ExecuteCommand(std::string &commandName, std::vector<double> &params, bool testOnly);
68+
virtual std::map<std::string, command_t> GetCommands();
6969

70-
std::map<std::string, std::tuple<double, uint64_t>> GetLatestSensorData();
70+
virtual std::map<std::string, std::tuple<double, uint64_t>> GetLatestSensorData();
7171
};

include/SequenceManager.h

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
#include "common.h"
44
#include <atomic>
5+
#include <optional>
6+
#include <utility/FileSystemAbstraction.h>
57

68
#include "utility/json.hpp"
79
#include "utility/Logging.h"
@@ -59,6 +61,36 @@ class SequenceManager : public Singleton<SequenceManager>
5961

6062
void plotMaps(uint8_t option);
6163

64+
/**
65+
* Interpolate linearly between start and end values.
66+
*
67+
* @param startTimeAndValues A tuple containing the start time and values.
68+
* The first element is the start time,
69+
* and the second element is a vector of values.
70+
* @param endTimeAndValues An optional tuple containing the end time and values.
71+
* The first element is the end time,
72+
* and the second element is a vector of values.
73+
* @param currentTime The current time.
74+
* @return A tuple containing:
75+
* - A vector of interpolated values, if one could be computed.
76+
* - A boolean indicating whether the interpolation is complete. If true,
77+
* the end value should be used as the new start value for the next interpolation.
78+
*/
79+
static std::tuple<std::optional<std::vector<double>>, bool> interpolateLinear(
80+
const std::tuple<int64_t, std::vector<double>> &startTimeAndValues,
81+
const std::optional<std::tuple<int64_t, std::vector<double>>>& endTimeAndValues,
82+
int64_t currentTime
83+
);
84+
85+
/**
86+
* No interpolation. Conforms to the api of interpolateLinear.
87+
*/
88+
static std::tuple<std::optional<std::vector<double>>, bool> interpolateNone(
89+
const std::tuple<int64_t, std::vector<double>> &startTimeAndValues,
90+
const std::optional<std::tuple<int64_t, std::vector<double>>>& endTimeAndValues,
91+
int64_t currentTime
92+
);
93+
6294
bool sequenceRunning = false;
6395
bool sequenceToStop = false;
6496

@@ -76,10 +108,10 @@ class SequenceManager : public Singleton<SequenceManager>
76108
nlohmann::json jsonSequence = nlohmann::json::object();
77109
nlohmann::json jsonAbortSequence = nlohmann::json::object();
78110

79-
std::string comments = "";
80-
std::string currentDirPath = "";
81-
std::string logFileName = "";
82-
std::string lastDir = "";
111+
std::string comments;
112+
std::string currentDirPath;
113+
std::string logFileName;
114+
std::string lastDir;
83115

84116
std::atomic_bool isInitialized = false;
85117

@@ -95,6 +127,7 @@ class SequenceManager : public Singleton<SequenceManager>
95127

96128
LLInterface *llInterface = nullptr;
97129
EventManager *eventManager = nullptr;
98-
130+
FileSystemAbstraction *fileSystem = nullptr;
131+
99132
std::thread sequenceThread;
100133
};

include/driver/Socket.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ class Socket
3131

3232
~Socket();
3333

34-
void Send(std::string msg);
34+
void Send(const std::string &msg);
3535
std::string Recv();
3636
// std::vector<uint8_t> RecvBytes();
3737
bool isConnectionActive();

include/utility/Config.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
class Config
1313
{
14-
private:
14+
protected:
1515
static const std::string CONFIG_FILE_NAME;
1616
static const std::string MAPPING_FILE_NAME;
1717

include/utility/Debug.h

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
#ifndef TXV_ECUI_LLSERVER_DEBUG_H
66
#define TXV_ECUI_LLSERVER_DEBUG_H
77

8+
#include <cstdint>
9+
#include <cstdio>
10+
#include <fstream>
811
#include <mutex>
12+
#include <sstream>
913
#include <string>
10-
#include <stdint.h>
11-
#include <stdio.h>
12-
#include <stdarg.h>
13-
#include <unistd.h>
1414
#include <thread>
15-
#include <fstream>
16-
#include <sstream>
15+
#include <unistd.h>
1716

1817
#include "common.h"
1918
#include "utility/Config.h"
@@ -47,14 +46,14 @@ class Debug {
4746

4847
static void close();
4948
static void flush();
50-
static void changeOutputFile(std::string outFilePath);
51-
static void log(std::string msg);
49+
static void changeOutputFile(const std::string &outFilePath);
50+
static void log(const std::string &msg);
5251

53-
static int32_t print(std::string fmt, ...);
52+
static int32_t print(const std::string &fmt, ...);
5453
static int32_t printNoTime(std::string fmt, ...);
55-
static int32_t error(std::string fmt, ...);
56-
static int32_t info(std::string fmt, ...);
57-
static int32_t warning(std::string fmt, ...);
54+
static int32_t error(const std::string &fmt, ...);
55+
static int32_t info(const std::string &fmt, ...);
56+
static int32_t warning(const std::string& fmt, ...);
5857

5958
};
6059

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#ifndef FILESYSTEM_H
2+
#define FILESYSTEM_H
3+
4+
#include <string>
5+
6+
#include "utility/Singleton.h"
7+
8+
class FileSystemAbstraction : public Singleton<FileSystemAbstraction> {
9+
friend class Singleton;
10+
11+
public:
12+
virtual ~FileSystemAbstraction() = default;
13+
14+
virtual std::string LoadFile(const std::string &filePath);
15+
16+
virtual void SaveFile(const std::string &filePath, const std::string &content);
17+
18+
virtual void CopyFile(const std::string &src, const std::string &dst);
19+
20+
virtual void CreateDirectory(const std::string &dirPath);
21+
22+
};
23+
24+
25+
26+
27+
#endif //FILESYSTEM_H

0 commit comments

Comments
 (0)