Mock driver for testing without hardware (Linux/Raspberry Pi).
Mock driver for testing without hardware (Linux/Raspberry Pi).Used for CI testing, unit tests, and development without physical displays.
#pragma once
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <expected>
#include <filesystem>
#include <format>
#include <span>
#include <vector>
extern "C" auto stbi_write_png(
char const *filename,
int w,
int h,
int comp,
const void *data,
int stride_in_bytes)
-> int;
class MockDriver {
public:
explicit MockDriver(Device &device, std::size_t
width = 600, std::size_t
height = 300,
bool save_images = true) noexcept
: spi_(device.get_spi()), cs_(device.get_output(Pin{0})),
dc_(device.get_output(Pin{1})),
rst_(device.get_output(Pin{2})),
busy_(device.get_input(Pin{3})),
output_dir_("mock_outputs") {
if (save_images_) {
std::filesystem::create_directories(output_dir_);
}
}
++init_count_;
if (init_should_fail_) {
}
initialized_ = true;
is_asleep_ = false;
return {};
}
[[nodiscard]]
auto clear() -> std::expected<void, Error> {
++clear_count_;
if (!initialized_) {
}
return {};
}
[[nodiscard]]
auto display(std::span<const std::byte> buffer) -> std::expected<void, Error> {
++display_count_;
if (!initialized_) {
}
if (display_should_fail_) {
}
if (is_asleep_) {
if (
auto result =
wake(); !result) {
return result;
}
}
last_buffer_ = std::vector<std::byte>(buffer.begin(), buffer.end());
if (save_images_) {
save_buffer_as_png(buffer);
}
return {};
}
[[nodiscard]]
auto display_planes(std::span<
const std::span<const std::byte>> planes) -> std::expected<void, Error> {
if (planes.empty()) {
}
if (planes.size() == 1) {
}
std::vector<std::byte> combined_buffer;
std::size_t total_size = 0;
for (const auto &plane : planes) {
total_size += plane.size();
}
combined_buffer.reserve(total_size);
for (const auto &plane : planes) {
combined_buffer.insert(combined_buffer.end(), plane.begin(), plane.end());
}
}
[[nodiscard]]
auto sleep() -> std::expected<void, Error> {
++sleep_count_;
if (sleep_should_fail_) {
}
is_asleep_ = true;
return {};
}
[[nodiscard]]
auto wake() -> std::expected<void, Error> {
++wake_count_;
if (wake_should_fail_) {
}
is_asleep_ = false;
return {};
}
[[nodiscard]]
auto power_off() -> std::expected<void, Error> {
return {};
}
[[nodiscard]]
auto power_on() -> std::expected<void, Error> {
return {};
}
[[nodiscard]]
auto width()
const noexcept -> std::size_t {
return width_; }
[[nodiscard]]
auto height()
const noexcept -> std::size_t {
return height_; }
[[nodiscard]]
auto buffer_size()
const noexcept -> std::size_t {
return (width_ * height_ * bpp + 7) / 8;
}
[[nodiscard]]
auto supports_wake()
const noexcept ->
bool {
return true; }
[[nodiscard]]
auto init_called()
const noexcept ->
bool {
return init_count_ > 0; }
[[nodiscard]]
auto display_called()
const noexcept ->
bool {
return display_count_ > 0; }
[[nodiscard]]
auto sleep_called()
const noexcept ->
bool {
return sleep_count_ > 0; }
[[nodiscard]]
auto wake_called()
const noexcept ->
bool {
return wake_count_ > 0; }
[[nodiscard]]
auto clear_called()
const noexcept ->
bool {
return clear_count_ > 0; }
[[nodiscard]]
auto init_count()
const noexcept -> std::size_t {
return init_count_; }
[[nodiscard]]
auto display_count()
const noexcept -> std::size_t {
return display_count_; }
[[nodiscard]]
auto sleep_count()
const noexcept -> std::size_t {
return sleep_count_; }
[[nodiscard]]
auto wake_count()
const noexcept -> std::size_t {
return wake_count_; }
[[nodiscard]]
auto clear_count()
const noexcept -> std::size_t {
return clear_count_; }
[[nodiscard]]
auto is_asleep()
const noexcept ->
bool {
return is_asleep_; }
[[nodiscard]]
auto is_initialized()
const noexcept ->
bool {
return initialized_; }
[[nodiscard]]
auto last_buffer()
const noexcept ->
const std::vector<std::byte> & {
return last_buffer_; }
init_count_ = 0;
display_count_ = 0;
sleep_count_ = 0;
wake_count_ = 0;
clear_count_ = 0;
}
output_dir_ = dir;
if (save_images_) {
std::filesystem::create_directories(output_dir_);
}
}
private:
auto save_buffer_as_png(std::span<const std::byte> buffer) const -> void {
std::filesystem::create_directories(output_dir_);
const auto now = std::chrono::system_clock::now();
const auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
const auto filename = std::format("{}/frame_{:013d}.png", output_dir_, timestamp);
auto render_width = width_;
auto render_height = height_;
if (!buffer.empty() && height_ > 0) {
const auto bytes_per_row = buffer.size() / height_;
if (mode_bpp == 1 && bytes_per_row * height_ == buffer.size()) {
render_width = bytes_per_row * 8;
}
if (mode_bpp == 2 && bytes_per_row * height_ == buffer.size()) {
render_width = bytes_per_row * 4;
}
}
std::vector<std::uint8_t> rgb_data;
rgb_data.reserve(render_width * render_height * 3);
for (std::size_t y = 0; y < render_height; ++y) {
for (std::size_t x = 0; x < render_width; ++x) {
rgb_data.push_back(rgb.r);
rgb_data.push_back(rgb.g);
rgb_data.push_back(rgb.b);
}
}
stbi_write_png(filename.c_str(),
static_cast<int>(render_width),
static_cast<int>(render_height), 3,
rgb_data.data(), static_cast<int>(render_width * 3));
}
Device::HalSpi spi_;
Device::HalOutput cs_;
Device::HalOutput dc_;
Device::HalOutput rst_;
Device::HalInput busy_;
std::size_t width_;
std::size_t height_;
bool initialized_{};
bool is_asleep_{};
bool init_should_fail_{};
bool display_should_fail_{};
bool sleep_should_fail_{};
bool wake_should_fail_{};
std::size_t init_count_{};
std::size_t display_count_{};
std::size_t sleep_count_{};
std::size_t wake_count_{};
std::size_t clear_count_{};
std::vector<std::byte> last_buffer_;
bool save_images_;
std::string output_dir_;
};
template <> struct driver_traits<MockDriver> {
static constexpr bool supports_grayscale = true;
static constexpr bool supports_partial_refresh = true;
static constexpr bool supports_power_control = true;
static constexpr bool supports_wake_from_sleep = true;
static constexpr std::size_t max_width = 800;
static constexpr std::size_t max_height = 600;
};
}
Compile-time driver capability traits system.
auto init_count() const noexcept -> std::size_t
Definition mock_driver.hpp:209
auto clear_called() const noexcept -> bool
Definition mock_driver.hpp:207
auto is_asleep() const noexcept -> bool
Definition mock_driver.hpp:219
auto display_planes(std::span< const std::span< const std::byte > > planes) -> std::expected< void, Error >
Definition mock_driver.hpp:116
auto is_initialized() const noexcept -> bool
Definition mock_driver.hpp:221
auto wake_called() const noexcept -> bool
Definition mock_driver.hpp:205
auto wake_count() const noexcept -> std::size_t
Definition mock_driver.hpp:215
auto sleep_called() const noexcept -> bool
Definition mock_driver.hpp:203
auto init_called() const noexcept -> bool
Definition mock_driver.hpp:199
auto display_count() const noexcept -> std::size_t
Definition mock_driver.hpp:211
auto power_off() -> std::expected< void, Error >
Definition mock_driver.hpp:161
auto clear() -> std::expected< void, Error >
Definition mock_driver.hpp:81
auto configure_wake_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:196
auto supports_partial_refresh() const noexcept -> bool
Definition mock_driver.hpp:183
auto init(DisplayMode mode) -> std::expected< void, Error >
Definition mock_driver.hpp:70
auto display_called() const noexcept -> bool
Definition mock_driver.hpp:201
auto power_on() -> std::expected< void, Error >
Definition mock_driver.hpp:166
auto clear_count() const noexcept -> std::size_t
Definition mock_driver.hpp:217
auto configure_display_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:192
auto display(std::span< const std::byte > buffer) -> std::expected< void, Error >
Definition mock_driver.hpp:89
auto height() const noexcept -> std::size_t
Definition mock_driver.hpp:174
auto reset_counts() noexcept -> void
Definition mock_driver.hpp:225
auto mode() const noexcept -> DisplayMode
Definition mock_driver.hpp:176
auto set_output_directory(std::string_view dir) -> void
Definition mock_driver.hpp:233
auto wake() -> std::expected< void, Error >
Definition mock_driver.hpp:152
auto configure_init_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:190
auto width() const noexcept -> std::size_t
Definition mock_driver.hpp:172
auto buffer_size() const noexcept -> std::size_t
Definition mock_driver.hpp:178
auto sleep_count() const noexcept -> std::size_t
Definition mock_driver.hpp:213
auto supports_wake() const noexcept -> bool
Definition mock_driver.hpp:187
auto enable_image_saving(bool enable) -> void
Definition mock_driver.hpp:240
auto sleep() -> std::expected< void, Error >
Definition mock_driver.hpp:143
auto supports_power_control() const noexcept -> bool
Definition mock_driver.hpp:185
auto last_buffer() const noexcept -> const std::vector< std::byte > &
Definition mock_driver.hpp:223
auto configure_sleep_failure(bool should_fail) noexcept -> void
Definition mock_driver.hpp:194
auto stbi_write_png(char const *filename, int w, int h, int comp, const void *data, int stride_in_bytes) -> int
@ DriverNotInitialized
Driver has not been initialized.
@ InvalidDimensions
Invalid image dimensions.
@ DriverInitFailed
Driver initialization failed.
@ TransferFailed
SPI/data transfer failed.
@ RefreshFailed
Display refresh operation failed.
constexpr auto bits_per_pixel(DisplayMode mode) noexcept -> std::uint8_t
Get bits per pixel for a display mode.
Definition driver.hpp:60
constexpr auto color_to_rgb(Color color) noexcept -> RGB
Convert a Color enum to RGB values.
Definition pixel_codec.hpp:154
DisplayMode
Definition driver.hpp:46
@ Spectra6
6-color: Black, White, Red, Yellow, Blue, Green (3-bit)
@ BlackWhite
1-bit black and white (2 colors)
auto get_pixel_from_buffer(DisplayMode mode, std::span< const std::byte > buffer, std::size_t width, std::size_t height, std::size_t x, std::size_t y) noexcept -> Color
Read a pixel from a buffer based on display mode.
Definition pixel_codec.hpp:569
Centralized pixel encoding/decoding utilities for framebuffers.