Skip to content
Merged
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
19 changes: 19 additions & 0 deletions .github/workflows/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,25 @@ jobs:
- name: Run Unit Tests
run: platformio test -e native-test

screenshot-tests:
name: Screenshot Regression Tests
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
with:
token: ${{ github.token }}
show-progress: false
- name: Install Dependencies
run: sudo apt-get update && sudo apt-get install -y cmake g++
- name: Build & Run Screenshot Tests
run: ./test/test_screenshots/build_and_run.sh
- name: Upload Screenshots on Failure
if: failure()
uses: actions/upload-artifact@v7
with:
name: screenshot-regression-output
path: test/test_screenshots/output/

pio-build:
name: PlatformIO Build
runs-on: ubuntu-latest
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,6 @@
eppg-controller.ino.cpp
src/sp140/sp140.ino.cpp
diagnostics-logs/
build-screenshot/
test/test_screenshots/output/*.bmp
test/test_screenshots/output/png/
2 changes: 1 addition & 1 deletion inc/sp140/lvgl/lv_conf.h
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@
* WIDGET USAGE
*=================*/

#define LV_USE_ANIMIMAGE 0
#define LV_USE_ANIMIMG 0

#define LV_USE_ARC 1

Expand Down
24 changes: 23 additions & 1 deletion platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,29 @@ build_flags =
-I .pio/libdeps/native-test/CircularBuffer
-D ARDUINO=1
build_src_filter = -<*>
test_ignore = neopixel*
test_ignore = neopixel*, test_screenshots
lib_deps =
ArduinoJson@7.4.3
rlogiacco/CircularBuffer@^1.4.0

[env:native-screenshot]
platform = native
test_framework = googletest
build_type = debug
build_flags =
-I test/screenshot_stubs
-I inc
-I inc/sp140/lvgl
-D LV_CONF_INCLUDE_SIMPLE
-D LV_LVGL_H_INCLUDE_SIMPLE
-D ARDUINO=1
-D NATIVE_SCREENSHOT_TEST=1
-std=c++17
build_src_filter =
-<*>
+<sp140/lvgl/lvgl_main_screen.cpp>
+<sp140/lvgl/lvgl_updates.cpp>
+<sp140/lvgl/lvgl_alerts.cpp>
test_filter = test_screenshots
lib_deps =
lvgl/lvgl@^9.5.0
21 changes: 21 additions & 0 deletions test/screenshot_stubs/Adafruit_ST7735.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
#pragma once

#include <stdint.h>
#include "SPI.h"

#define INITR_BLACKTAB 0
#define ST77XX_BLACK 0x0000

class Adafruit_ST7735 {
public:
Adafruit_ST7735(SPIClass* spi, int8_t cs, int8_t dc, int8_t rst) {
(void)spi; (void)cs; (void)dc; (void)rst;
}
void initR(uint8_t) {}
void setRotation(uint8_t) {}
void fillScreen(uint16_t) {}
void setAddrWindow(uint16_t, uint16_t, uint16_t, uint16_t) {}
void writePixels(uint16_t*, uint32_t) {}
void startWrite() {}
void endWrite() {}
};
129 changes: 129 additions & 0 deletions test/screenshot_stubs/Arduino.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
#pragma once
#include <stdint.h>
#include <chrono>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <string>
#include <thread>

// Basic Arduino types and helpers for native screenshot tests
using std::chrono::steady_clock;
using std::chrono::milliseconds;

class String {
public:
String() = default;
String(const char* s) : data_(s ? s : "") {}
bool concat(char c) { data_ += c; return true; }
bool concat(const char* s) { data_ += s ? s : ""; return true; }
const char* c_str() const { return data_.c_str(); }
size_t length() const { return data_.size(); }
private:
std::string data_;
};

struct __FlashStringHelper;

class Print {
public:
virtual ~Print() = default;
virtual size_t write(uint8_t) { return 1; }
virtual size_t write(const uint8_t* buffer, size_t size) { (void)buffer; return size; }
size_t write(const char* s) { return write(reinterpret_cast<const uint8_t*>(s), s ? strlen(s) : 0); }
template <typename... Args> void printf(const char*, Args...) {}
template <typename T> void print(const T&) {}
template <typename T> void println(const T&) {}
void println() {}
};

class Printable {
public:
virtual ~Printable() = default;
virtual size_t printTo(Print&) const { return 0; }
};

class Stream : public Print {
public:
virtual int available() { return 0; }
virtual int read() { return -1; }
virtual size_t readBytes(char* buffer, size_t length) { (void)buffer; (void)length; return 0; }
};

struct DummySerial : public Stream {
inline void begin(unsigned long) {}
};

static DummySerial USBSerial;
static DummySerial Serial;

#ifndef PROGMEM
#define PROGMEM
#endif

#ifndef pgm_read_byte
inline uint8_t pgm_read_byte(const void* p) { return *reinterpret_cast<const uint8_t*>(p); }
#endif

inline unsigned long millis() {
static auto start = steady_clock::now();
return (unsigned long)std::chrono::duration_cast<milliseconds>(steady_clock::now() - start).count();
}

inline void delay(unsigned long ms) {
std::this_thread::sleep_for(std::chrono::milliseconds(ms));
}

typedef uint16_t word;

// FreeRTOS type stubs
typedef void* QueueHandle_t;
typedef void* TaskHandle_t;

// GPIO stubs
#ifndef INPUT
#define INPUT 0
#endif
#ifndef OUTPUT
#define OUTPUT 1
#endif
#ifndef LOW
#define LOW 0
#endif
#ifndef HIGH
#define HIGH 1
#endif
#ifndef F
#define F(x) x
#endif

inline void pinMode(int, int) {}
inline void digitalWrite(int, int) {}
inline int digitalRead(int) { return 0; }
inline int analogRead(int) { return 0; }
inline void analogReadResolution(int) {}

// Math helpers
#ifndef constrain
template <typename T>
inline T constrain(T x, T a, T b) { return x < a ? a : (x > b ? b : x); }
#endif

#ifndef min
template <typename T>
inline T min(T a, T b) { return a < b ? a : b; }
#endif

#ifndef max
template <typename T>
inline T max(T a, T b) { return a > b ? a : b; }
#endif

inline long map(long x, long in_min, long in_max, long out_min, long out_max) {
return (x - in_min) * (out_max - out_min) / (in_max - in_min) + out_min;
}

// LEDC stubs (ESP32)
inline void ledcAttach(int, int, int) {}
inline void ledcWrite(int, int) {}
inline void ledcDetach(int) {}
10 changes: 10 additions & 0 deletions test/screenshot_stubs/BMS_CAN.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <stdint.h>

// Stub BMS_CAN class for native builds
class BMS_CAN {
public:
static constexpr float TEMP_PROBE_DISCONNECTED = -999.0f;
BMS_CAN() {}
};
24 changes: 24 additions & 0 deletions test/screenshot_stubs/NimBLEDevice.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#pragma once

#include <stdint.h>

// Minimal NimBLE stubs for native builds
class NimBLECharacteristic {
public:
void setValue(const uint8_t*, size_t) {}
void notify() {}
};

class NimBLEServer {
public:
uint16_t getConnectedCount() { return 0; }
};

class NimBLEService {};
class NimBLEAdvertising {};

class NimBLEDevice {
public:
static void init(const char*) {}
static NimBLEServer* createServer() { return nullptr; }
};
2 changes: 2 additions & 0 deletions test/screenshot_stubs/NimBLEServer.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
#include "NimBLEDevice.h"
2 changes: 2 additions & 0 deletions test/screenshot_stubs/NimBLEUtils.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
// Stub - no content needed
14 changes: 14 additions & 0 deletions test/screenshot_stubs/SPI.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
#pragma once

#include <stdint.h>

#define HSPI 2

class SPIClass {
public:
SPIClass(int bus = 0) { (void)bus; }
void begin(int sck = -1, int miso = -1, int mosi = -1, int ss = -1) {
(void)sck; (void)miso; (void)mosi; (void)ss;
}
void end() {}
};
35 changes: 35 additions & 0 deletions test/screenshot_stubs/freertos/FreeRTOS.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#pragma once

#include <stdint.h>

// FreeRTOS type stubs for native builds
typedef void* SemaphoreHandle_t;
typedef void* QueueHandle_t;
typedef void* TaskHandle_t;
typedef uint32_t TickType_t;
typedef int32_t BaseType_t;
typedef uint32_t UBaseType_t;

#define pdTRUE 1
#define pdFALSE 0
#define pdPASS pdTRUE
#define portMAX_DELAY 0xFFFFFFFF

inline TickType_t pdMS_TO_TICKS(uint32_t ms) { return ms; }

// Semaphore stubs
inline SemaphoreHandle_t xSemaphoreCreateMutex() { return (void*)1; }
inline BaseType_t xSemaphoreTake(SemaphoreHandle_t, TickType_t) { return pdTRUE; }
inline BaseType_t xSemaphoreGive(SemaphoreHandle_t) { return pdTRUE; }

// Task stubs
inline void vTaskDelay(TickType_t) {}
inline void vTaskDelete(TaskHandle_t) {}

// Queue stubs
inline QueueHandle_t xQueueCreate(UBaseType_t, UBaseType_t) { return (void*)1; }
inline BaseType_t xQueueSend(QueueHandle_t, const void*, TickType_t) { return pdTRUE; }
inline BaseType_t xQueueReceive(QueueHandle_t, void*, TickType_t) { return pdFALSE; }
inline BaseType_t xQueueOverwrite(QueueHandle_t, const void*) { return pdTRUE; }
inline UBaseType_t uxQueueMessagesWaiting(QueueHandle_t) { return 0; }
inline void xQueueReset(QueueHandle_t) {}
2 changes: 2 additions & 0 deletions test/screenshot_stubs/freertos/queue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
#include "FreeRTOS.h"
2 changes: 2 additions & 0 deletions test/screenshot_stubs/freertos/semphr.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
#include "FreeRTOS.h"
2 changes: 2 additions & 0 deletions test/screenshot_stubs/freertos/task.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#pragma once
#include "FreeRTOS.h"
Loading
Loading