mirror of
https://github.com/dalathegreat/Battery-Emulator.git
synced 2025-10-05 19:42:08 +02:00
Improvements and unit tests
This commit is contained in:
parent
4d95fcfc31
commit
44c02745bf
9 changed files with 425 additions and 2 deletions
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
||||||
|
|
||||||
# Ignore any files in the build folder
|
# Ignore any files in the build folder
|
||||||
Software/build/
|
Software/build/
|
||||||
|
|
||||||
|
*.exe
|
|
@ -12,7 +12,7 @@
|
||||||
//#define CHADEMO_BATTERY
|
//#define CHADEMO_BATTERY
|
||||||
//#define IMIEV_CZERO_ION_BATTERY
|
//#define IMIEV_CZERO_ION_BATTERY
|
||||||
//#define KIA_HYUNDAI_64_BATTERY
|
//#define KIA_HYUNDAI_64_BATTERY
|
||||||
//#define NISSAN_LEAF_BATTERY
|
#define NISSAN_LEAF_BATTERY
|
||||||
//#define RENAULT_KANGOO_BATTERY
|
//#define RENAULT_KANGOO_BATTERY
|
||||||
//#define RENAULT_ZOE_BATTERY
|
//#define RENAULT_ZOE_BATTERY
|
||||||
//#define SANTA_FE_PHEV_BATTERY
|
//#define SANTA_FE_PHEV_BATTERY
|
||||||
|
@ -21,7 +21,7 @@
|
||||||
|
|
||||||
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
|
/* Select inverter communication protocol. See Wiki for which to use with your inverter: https://github.com/dalathegreat/BYD-Battery-Emulator-For-Gen24/wiki */
|
||||||
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
|
//#define BYD_CAN //Enable this line to emulate a "BYD Battery-Box Premium HVS" over CAN Bus
|
||||||
//#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
|
#define BYD_MODBUS //Enable this line to emulate a "BYD 11kWh HVM battery" over Modbus RTU
|
||||||
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
|
//#define LUNA2000_MODBUS //Enable this line to emulate a "Luna2000 battery" over Modbus RTU
|
||||||
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
|
//#define PYLON_CAN //Enable this line to emulate a "Pylontech battery" over CAN bus
|
||||||
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus
|
//#define SMA_CAN //Enable this line to emulate a "BYD Battery-Box H 8.9kWh, 7 mod" over CAN bus
|
||||||
|
|
|
@ -1,27 +1,48 @@
|
||||||
#include "events.h"
|
#include "events.h"
|
||||||
|
|
||||||
|
#include "../../../USER_SETTINGS.h"
|
||||||
|
#include "../config.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint32_t timestamp; // Time in seconds since startup when the event occurred
|
uint32_t timestamp; // Time in seconds since startup when the event occurred
|
||||||
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
|
uint8_t data; // Custom data passed when setting the event, for example cell number for under voltage
|
||||||
uint8_t occurences; // Number of occurrences since startup
|
uint8_t occurences; // Number of occurrences since startup
|
||||||
|
uint8_t led_color; // Weirdly indented comment
|
||||||
} EVENTS_STRUCT_TYPE;
|
} EVENTS_STRUCT_TYPE;
|
||||||
|
|
||||||
static EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
|
static EVENTS_STRUCT_TYPE entries[EVENT_NOF_EVENTS];
|
||||||
static unsigned long previous_millis = 0;
|
static unsigned long previous_millis = 0;
|
||||||
static uint32_t time_seconds = 0;
|
static uint32_t time_seconds = 0;
|
||||||
|
static uint8_t total_led_color = GREEN;
|
||||||
|
|
||||||
|
/* Local function prototypes */
|
||||||
|
static void print_event_message(EVENTS_ENUM_TYPE event);
|
||||||
|
static void update_led_color(EVENTS_ENUM_TYPE event);
|
||||||
|
|
||||||
|
/* Exported functions */
|
||||||
void init_events(void) {
|
void init_events(void) {
|
||||||
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
|
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
|
||||||
entries[i].timestamp = 0;
|
entries[i].timestamp = 0;
|
||||||
entries[i].data = 0;
|
entries[i].data = 0;
|
||||||
entries[i].occurences = 0;
|
entries[i].occurences = 0;
|
||||||
|
entries[i].led_color = RED; // Most events are RED
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// YELLOW events below
|
||||||
|
entries[EVENT_12V_LOW].led_color = YELLOW;
|
||||||
|
entries[EVENT_CAN_WARNING].led_color = YELLOW;
|
||||||
|
entries[EVENT_CELL_DEVIATION_HIGH].led_color = YELLOW;
|
||||||
|
entries[EVENT_KWH_PLAUSIBILITY_ERROR].led_color = YELLOW;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
|
void set_event(EVENTS_ENUM_TYPE event, uint8_t data) {
|
||||||
|
if (event >= EVENT_NOF_EVENTS) {
|
||||||
|
event = EVENT_UNKNOWN_EVENT_SET;
|
||||||
|
}
|
||||||
entries[event].timestamp = time_seconds;
|
entries[event].timestamp = time_seconds;
|
||||||
entries[event].data = data;
|
entries[event].data = data;
|
||||||
entries[event].occurences++;
|
entries[event].occurences++;
|
||||||
|
print_event_message(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
void update_event_timestamps(void) {
|
void update_event_timestamps(void) {
|
||||||
|
@ -31,3 +52,75 @@ void update_event_timestamps(void) {
|
||||||
previous_millis = new_millis;
|
previous_millis = new_millis;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Local functions */
|
||||||
|
static void update_led_color(EVENTS_ENUM_TYPE event) {
|
||||||
|
total_led_color = (total_led_color == RED) ? RED : entries[event].led_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void print_event_message(EVENTS_ENUM_TYPE event) {
|
||||||
|
#ifndef DEBUG_VIA_USB
|
||||||
|
return;
|
||||||
|
#else
|
||||||
|
switch (event) {
|
||||||
|
case EVENT_CAN_FAILURE:
|
||||||
|
Serial.println("No CAN communication detected for 60s. Shutting down battery control.");
|
||||||
|
break;
|
||||||
|
case EVENT_CAN_WARNING:
|
||||||
|
Serial.println("ERROR: High amount of corrupted CAN messages detected. Check CAN wire shielding!");
|
||||||
|
break;
|
||||||
|
case EVENT_WATER_INGRESS:
|
||||||
|
Serial.println("Water leakage inside battery detected. Operation halted. Inspect battery!");
|
||||||
|
break;
|
||||||
|
case EVENT_12V_LOW:
|
||||||
|
Serial.println(
|
||||||
|
"12V battery source below required voltage to safely close contactors. Inspect the supply/battery!");
|
||||||
|
break;
|
||||||
|
case EVENT_SOC_PLAUSIBILITY_ERROR:
|
||||||
|
Serial.println("ERROR: SOC% reported by battery not plausible. Restart battery!");
|
||||||
|
break;
|
||||||
|
case EVENT_KWH_PLAUSIBILITY_ERROR:
|
||||||
|
Serial.println("Warning: kWh remaining reported by battery not plausible. Battery needs cycling.");
|
||||||
|
break;
|
||||||
|
case EVENT_BATTERY_CHG_STOP_REQ:
|
||||||
|
Serial.println("ERROR: Battery raised caution indicator AND requested charge stop. Inspect battery status!");
|
||||||
|
break;
|
||||||
|
case EVENT_BATTERY_DISCHG_STOP_REQ:
|
||||||
|
Serial.println("ERROR: Battery raised caution indicator AND requested discharge stop. Inspect battery status!");
|
||||||
|
break;
|
||||||
|
case EVENT_BATTERY_CHG_DISCHG_STOP_REQ:
|
||||||
|
Serial.println(
|
||||||
|
"ERROR: Battery raised caution indicator AND requested charge/discharge stop. Inspect battery status!");
|
||||||
|
break;
|
||||||
|
case EVENT_LOW_SOH:
|
||||||
|
Serial.println(
|
||||||
|
"ERROR: State of health critically low. Battery internal resistance too high to continue. Recycle battery.");
|
||||||
|
break;
|
||||||
|
case EVENT_HVIL_FAILURE:
|
||||||
|
Serial.println(
|
||||||
|
"ERROR: Battery interlock loop broken. Check that high voltage connectors are seated. Battery will be "
|
||||||
|
"disabled!");
|
||||||
|
break;
|
||||||
|
case EVENT_INTERNAL_OPEN_FAULT:
|
||||||
|
Serial.println("ERROR: High voltage cable removed while battery running. Opening contactors!");
|
||||||
|
break;
|
||||||
|
case EVENT_CELL_UNDER_VOLTAGE:
|
||||||
|
Serial.println("ERROR: CELL UNDERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
|
break;
|
||||||
|
case EVENT_CELL_OVER_VOLTAGE:
|
||||||
|
Serial.println("ERROR: CELL OVERVOLTAGE!!! Stopping battery charging and discharging. Inspect battery!");
|
||||||
|
break;
|
||||||
|
case EVENT_CELL_DEVIATION_HIGH:
|
||||||
|
Serial.println("ERROR: HIGH CELL DEVIATION!!! Inspect battery!");
|
||||||
|
break;
|
||||||
|
case EVENT_UNKNOWN_EVENT_SET:
|
||||||
|
Serial.println("An unknown event was set! Review your code!");
|
||||||
|
break;
|
||||||
|
case EVENT_DUMMY:
|
||||||
|
Serial.println("The dummy event was set!");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
#ifndef __EVENTS_H__
|
#ifndef __EVENTS_H__
|
||||||
#define __EVENTS_H__
|
#define __EVENTS_H__
|
||||||
|
|
||||||
|
#ifndef UNIT_TEST
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
EVENT_CAN_FAILURE = 0u,
|
EVENT_CAN_FAILURE = 0u,
|
||||||
|
@ -10,12 +12,16 @@ typedef enum {
|
||||||
EVENT_12V_LOW,
|
EVENT_12V_LOW,
|
||||||
EVENT_SOC_PLAUSIBILITY_ERROR,
|
EVENT_SOC_PLAUSIBILITY_ERROR,
|
||||||
EVENT_KWH_PLAUSIBILITY_ERROR,
|
EVENT_KWH_PLAUSIBILITY_ERROR,
|
||||||
|
EVENT_BATTERY_CHG_STOP_REQ,
|
||||||
|
EVENT_BATTERY_DISCHG_STOP_REQ,
|
||||||
EVENT_BATTERY_CHG_DISCHG_STOP_REQ,
|
EVENT_BATTERY_CHG_DISCHG_STOP_REQ,
|
||||||
EVENT_LOW_SOH,
|
EVENT_LOW_SOH,
|
||||||
EVENT_HVIL_FAILURE,
|
EVENT_HVIL_FAILURE,
|
||||||
EVENT_INTERNAL_OPEN_FAULT,
|
EVENT_INTERNAL_OPEN_FAULT,
|
||||||
EVENT_CELL_UNDER_VOLTAGE,
|
EVENT_CELL_UNDER_VOLTAGE,
|
||||||
EVENT_CELL_OVER_VOLTAGE,
|
EVENT_CELL_OVER_VOLTAGE,
|
||||||
|
EVENT_CELL_DEVIATION_HIGH,
|
||||||
|
EVENT_UNKNOWN_EVENT_SET,
|
||||||
EVENT_DUMMY,
|
EVENT_DUMMY,
|
||||||
EVENT_NOF_EVENTS
|
EVENT_NOF_EVENTS
|
||||||
} EVENTS_ENUM_TYPE;
|
} EVENTS_ENUM_TYPE;
|
||||||
|
|
65
Software/src/devboard/utils/events_test.cpp
Normal file
65
Software/src/devboard/utils/events_test.cpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
// The test library must be included first!
|
||||||
|
#include "../../../test/test_lib.h"
|
||||||
|
|
||||||
|
#include "events.cpp"
|
||||||
|
|
||||||
|
TEST(init_events_test) {
|
||||||
|
init_events();
|
||||||
|
|
||||||
|
for (uint8_t i = 0; i < EVENT_NOF_EVENTS; i++) {
|
||||||
|
ASSERT_EQ(entries[i].occurences, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(update_event_timestamps_test) {
|
||||||
|
// Reset
|
||||||
|
init_events();
|
||||||
|
time_seconds = 0;
|
||||||
|
|
||||||
|
// No delta, so time shouldn't increase
|
||||||
|
test_millis = 0;
|
||||||
|
update_event_timestamps();
|
||||||
|
ASSERT_EQ(time_seconds, 0);
|
||||||
|
|
||||||
|
// Almost time to bump the seconds
|
||||||
|
test_millis = 999;
|
||||||
|
update_event_timestamps();
|
||||||
|
ASSERT_EQ(time_seconds, 0);
|
||||||
|
ASSERT_EQ(previous_millis, 0);
|
||||||
|
|
||||||
|
// millis == 1000, so we should add a second
|
||||||
|
test_millis = 1000;
|
||||||
|
update_event_timestamps();
|
||||||
|
ASSERT_EQ(time_seconds, 1);
|
||||||
|
ASSERT_EQ(previous_millis, 1000);
|
||||||
|
|
||||||
|
// We shouldn't add more seconds until 2000 now
|
||||||
|
test_millis = 1999;
|
||||||
|
update_event_timestamps();
|
||||||
|
ASSERT_EQ(time_seconds, 1);
|
||||||
|
ASSERT_EQ(previous_millis, 1000);
|
||||||
|
test_millis = 2000;
|
||||||
|
update_event_timestamps();
|
||||||
|
ASSERT_EQ(time_seconds, 2);
|
||||||
|
ASSERT_EQ(previous_millis, 2000);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(set_event_test) {
|
||||||
|
// Reset
|
||||||
|
init_events();
|
||||||
|
time_seconds = 0;
|
||||||
|
|
||||||
|
// Initially, the event should not have any data or occurences
|
||||||
|
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].data, 0);
|
||||||
|
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].occurences, 0);
|
||||||
|
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 0);
|
||||||
|
// Set current time and overvoltage event for cell 23
|
||||||
|
time_seconds = 345;
|
||||||
|
set_event(EVENT_CELL_OVER_VOLTAGE, 123);
|
||||||
|
// Ensure proper event data
|
||||||
|
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].data, 123);
|
||||||
|
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].occurences, 1);
|
||||||
|
ASSERT_EQ(entries[EVENT_CELL_OVER_VOLTAGE].timestamp, 345);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_MAIN();
|
1
Software/src/devboard/utils/test_commands.txt
Normal file
1
Software/src/devboard/utils/test_commands.txt
Normal file
|
@ -0,0 +1 @@
|
||||||
|
g++ events_test.cpp ../../../test/test_lib.cpp -o events_test.exe -DUNIT_TEST -I.
|
209
Software/test/microtest.h
Normal file
209
Software/test/microtest.h
Normal file
|
@ -0,0 +1,209 @@
|
||||||
|
//
|
||||||
|
// microtest.h
|
||||||
|
//
|
||||||
|
// URL: https://github.com/torpedro/microtest.h
|
||||||
|
// Author: Pedro Flemming (http://torpedro.com/)
|
||||||
|
// License: MIT License (https://github.com/torpedro/microtest.h/blob/master/LICENSE)
|
||||||
|
// Copyright (c) 2017 Pedro Flemming
|
||||||
|
//
|
||||||
|
// This is a small header-only C++ unit testing framework.
|
||||||
|
// It allows to define small unit tests with set of assertions available.
|
||||||
|
//
|
||||||
|
#ifndef __MICROTEST_H__
|
||||||
|
#define __MICROTEST_H__
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// Assertions //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
#define ASSERT(cond) ASSERT_TRUE(cond);
|
||||||
|
|
||||||
|
#define ASSERT_TRUE(cond) \
|
||||||
|
if (!(cond)) \
|
||||||
|
throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
#define ASSERT_FALSE(cond) \
|
||||||
|
if (cond) \
|
||||||
|
throw mt::AssertFailedException(#cond, __FILE__, __LINE__);
|
||||||
|
|
||||||
|
#define ASSERT_NULL(value) ASSERT_TRUE(value == NULL);
|
||||||
|
|
||||||
|
#define ASSERT_NOTNULL(value) ASSERT_TRUE(value != NULL);
|
||||||
|
|
||||||
|
#define ASSERT_STREQ(a, b) \
|
||||||
|
if (std::string(a).compare(std::string(b)) != 0) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " != " << b << std::endl; \
|
||||||
|
throw mt::AssertFailedException(#a " == " #b, __FILE__, __LINE__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASSERT_STRNEQ(a, b) \
|
||||||
|
if (std::string(a).compare(std::string(b)) != = 0) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " == " << b << std::endl; \
|
||||||
|
throw mt::AssertFailedException(#a " != " #b, __FILE__, __LINE__); \
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ASSERT_EQ(a, b) \
|
||||||
|
if (a != b) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " != " << b << std::endl; \
|
||||||
|
} \
|
||||||
|
ASSERT(a == b);
|
||||||
|
|
||||||
|
#define ASSERT_NEQ(a, b) \
|
||||||
|
if (a == b) { \
|
||||||
|
printf("%s{ info} %s", mt::yellow(), mt::def()); \
|
||||||
|
std::cout << "Actual values: " << a << " == " << b << std::endl; \
|
||||||
|
} \
|
||||||
|
ASSERT(a != b);
|
||||||
|
|
||||||
|
////////////////
|
||||||
|
// Unit Tests //
|
||||||
|
////////////////
|
||||||
|
|
||||||
|
#define TEST(name) \
|
||||||
|
void name(); \
|
||||||
|
namespace { \
|
||||||
|
bool __##name = mt::TestsManager::AddTest(name, #name); \
|
||||||
|
} \
|
||||||
|
void name()
|
||||||
|
|
||||||
|
///////////////
|
||||||
|
// Framework //
|
||||||
|
///////////////
|
||||||
|
|
||||||
|
namespace mt {
|
||||||
|
|
||||||
|
inline const char* red() {
|
||||||
|
return "\033[1;31m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* green() {
|
||||||
|
return "\033[0;32m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* yellow() {
|
||||||
|
return "\033[0;33m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline const char* def() {
|
||||||
|
return "\033[0m";
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void printRunning(const char* message, FILE* file = stdout) {
|
||||||
|
fprintf(file, "%s{ running}%s %s\n", green(), def(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void printOk(const char* message, FILE* file = stdout) {
|
||||||
|
fprintf(file, "%s{ ok}%s %s\n", green(), def(), message);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void printFailed(const char* message, FILE* file = stdout) {
|
||||||
|
fprintf(file, "%s{ failed} %s%s\n", red(), message, def());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exception that is thrown when an assertion fails.
|
||||||
|
class AssertFailedException : public std::exception {
|
||||||
|
public:
|
||||||
|
AssertFailedException(std::string description, std::string filepath, int line)
|
||||||
|
: std::exception(), description_(description), filepath_(filepath), line_(line){};
|
||||||
|
|
||||||
|
virtual const char* what() const throw() { return description_.c_str(); }
|
||||||
|
|
||||||
|
inline const char* getFilepath() { return filepath_.c_str(); }
|
||||||
|
|
||||||
|
inline int getLine() { return line_; }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::string description_;
|
||||||
|
std::string filepath_;
|
||||||
|
int line_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TestsManager {
|
||||||
|
// Note: static initialization fiasco
|
||||||
|
// http://www.parashift.com/c++-faq-lite/static-init-order.html
|
||||||
|
// http://www.parashift.com/c++-faq-lite/static-init-order-on-first-use.html
|
||||||
|
public:
|
||||||
|
struct Test {
|
||||||
|
const char* name;
|
||||||
|
void (*fn)(void);
|
||||||
|
};
|
||||||
|
|
||||||
|
static std::vector<Test>& tests() {
|
||||||
|
static std::vector<Test> tests_;
|
||||||
|
return tests_;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Adds a new test to the current set of tests.
|
||||||
|
// Returns false if a test with the same name already exists.
|
||||||
|
inline static bool AddTest(void (*fn)(void), const char* name) {
|
||||||
|
tests().push_back({name, fn});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run all tests that are registered.
|
||||||
|
// Returns the number of tests that failed.
|
||||||
|
inline static size_t RunAllTests(FILE* file = stdout) {
|
||||||
|
size_t num_failed = 0;
|
||||||
|
|
||||||
|
for (const Test& test : tests()) {
|
||||||
|
// Run the test.
|
||||||
|
// If an AsserFailedException is thrown, the test has failed.
|
||||||
|
try {
|
||||||
|
printRunning(test.name, file);
|
||||||
|
|
||||||
|
(*test.fn)();
|
||||||
|
|
||||||
|
printOk(test.name, file);
|
||||||
|
|
||||||
|
} catch (AssertFailedException& e) {
|
||||||
|
printFailed(test.name, file);
|
||||||
|
fprintf(file, " %sAssertion failed: %s%s\n", red(), e.what(), def());
|
||||||
|
fprintf(file, " %s%s:%d%s\n", red(), e.getFilepath(), e.getLine(), def());
|
||||||
|
++num_failed;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int return_code = (num_failed > 0) ? 1 : 0;
|
||||||
|
return return_code;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Class that will capture the arguments passed to the program.
|
||||||
|
class Runtime {
|
||||||
|
public:
|
||||||
|
static const std::vector<std::string>& args(int argc = -1, char** argv = NULL) {
|
||||||
|
static std::vector<std::string> args_;
|
||||||
|
if (argc >= 0) {
|
||||||
|
for (int i = 0; i < argc; ++i) {
|
||||||
|
args_.push_back(argv[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return args_;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace mt
|
||||||
|
|
||||||
|
#define TEST_MAIN() \
|
||||||
|
int main(int argc, char* argv[]) { \
|
||||||
|
mt::Runtime::args(argc, argv); \
|
||||||
|
\
|
||||||
|
size_t num_failed = mt::TestsManager::RunAllTests(stdout); \
|
||||||
|
if (num_failed == 0) { \
|
||||||
|
fprintf(stdout, "%s{ summary} All tests succeeded!%s\n", mt::green(), mt::def()); \
|
||||||
|
return 0; \
|
||||||
|
} else { \
|
||||||
|
double percentage = 100.0 * num_failed / mt::TestsManager::tests().size(); \
|
||||||
|
fprintf(stderr, "%s{ summary} %lu tests failed (%.2f%%)%s\n", mt::red(), num_failed, percentage, mt::def()); \
|
||||||
|
return -1; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // __MICROTEST_H__
|
6
Software/test/test_lib.cpp
Normal file
6
Software/test/test_lib.cpp
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
#include "test_lib.h"
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
MySerial Serial;
|
||||||
|
|
||||||
|
unsigned long test_millis = 0;
|
41
Software/test/test_lib.h
Normal file
41
Software/test/test_lib.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#ifndef __TEST_LIB_H__
|
||||||
|
#define __TEST_LIB_H__
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <cstdio>
|
||||||
|
|
||||||
|
#include "microtest.h"
|
||||||
|
|
||||||
|
class MySerial;
|
||||||
|
|
||||||
|
extern unsigned long test_millis;
|
||||||
|
|
||||||
|
static inline unsigned long millis(void) {
|
||||||
|
return test_millis;
|
||||||
|
}
|
||||||
|
|
||||||
|
class MySerial {
|
||||||
|
public:
|
||||||
|
size_t println(const char* s) {
|
||||||
|
return print(s, true); // Call print with newline argument true
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t print(const char* s) {
|
||||||
|
return print(s, false); // Call print with newline argument false
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t print(const char* s, bool newline) {
|
||||||
|
size_t length = printf("%s", s); // Print the string without newline
|
||||||
|
if (newline) {
|
||||||
|
printf("\n"); // Add a newline if specified
|
||||||
|
length++; // Increment length to account for the added newline character
|
||||||
|
}
|
||||||
|
return length; // Return the total length printed
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
extern MySerial Serial;
|
||||||
|
|
||||||
|
#endif
|
Loading…
Add table
Add a link
Reference in a new issue