Skip to content

Commit 278245f

Browse files
committed
Unit-tests
1 parent 23298f8 commit 278245f

File tree

12 files changed

+421
-23
lines changed

12 files changed

+421
-23
lines changed

.github/workflows/build.yml

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,17 @@ jobs:
2525
with:
2626
submodules: recursive
2727

28+
- name: Cache debian
29+
id: cache-debian
30+
uses: actions/cache@v4
31+
with:
32+
path: |
33+
!.apt_cache/archives/lock
34+
!.apt_cache/archives/partial
35+
.apt_cache
36+
debian-*-generic-arm64.tar
37+
key: ${{ matrix.debian_codename }}-files
38+
2839
- name: Install deps
2940
run: |
3041
sudo apt-get update
@@ -55,6 +66,39 @@ jobs:
5566
${{ matrix.debian_codename }}_pixelpilot-rk_*_arm64.deb
5667
${{ matrix.debian_codename }}_pixelpilot-rk-dbgsym_*_arm64.deb
5768
69+
test:
70+
name: Test (${{ matrix.debian_codename }})
71+
runs-on: ubuntu-latest
72+
strategy:
73+
matrix:
74+
debian_codename:
75+
- bookworm
76+
steps:
77+
- name: Checkout
78+
uses: actions/checkout@v4
79+
with:
80+
submodules: recursive
81+
82+
- name: Cache debian
83+
id: cache-debian
84+
uses: actions/cache@v4
85+
with:
86+
path: |
87+
.apt_cache
88+
!.apt_cache/archives/lock
89+
!.apt_cache/archives/partial
90+
debian-*-generic-arm64.tar
91+
key: ${{ matrix.debian_codename }}-files
92+
93+
- name: Install deps
94+
run: |
95+
sudo apt-get update
96+
sudo apt-get install -y qemu-user-static
97+
98+
- name: QEMU Test
99+
run: |
100+
make qemu_test DEBIAN_CODENAME=${{ matrix.debian_codename }}
101+
58102
release:
59103
name: Release Artifacts
60104
runs-on: ubuntu-latest

CMakeLists.txt

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ find_package(PkgConfig REQUIRED)
66
set(CMAKE_CXX_STANDARD 17)
77
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
88

9+
# Option to control wmenu simulator build
10+
option(USE_SIMULATOR "Build the simulator version of the application" OFF)
11+
# If build should include tests
12+
option(BUILD_TESTS "Build unit tests" OFF)
13+
914
# LVGL setup
1015
set(LV_USE_LINUX_DRM ON)
1116
set(LV_CONF_BUILD_DISABLE_DEMOS 1)
@@ -73,7 +78,7 @@ set(SIMULATOR_SOURCES
7378

7479
add_compile_options("-Wno-address-of-packed-member")
7580

76-
set(SOURCE_FILES
81+
set(LIB_SOURCE_FILES
7782
src/drm.h
7883
src/drm.c
7984
src/gsmenu/executor.h
@@ -126,23 +131,22 @@ set(SOURCE_FILES
126131
src/dvr.cpp
127132
src/mavlink.h
128133
src/mavlink.c
129-
src/main.cpp
130-
src/main.h
131134
src/wfbcli.hpp
132135
src/wfbcli.cpp
133136
src/scheduling_helper.hpp
134137
src/gstrtpreceiver.cpp
135138
src/gstrtpreceiver.h)
139+
set(SOURCE_FILES
140+
${LIB_SOURCE_FILES}
141+
src/main.cpp
142+
src/main.h)
136143
file(GLOB ICONS src/icons/*.png)
137144
file(GLOB OSD_CONFIGS *_osd.json)
138145

139146
include_directories("/usr/include/libdrm" "/usr/include/cairo" "/usr/include/spdlog")
140147

141148
configure_file("${PROJECT_NAME}_config.h.in" "${PROJECT_NAME}_config.h")
142149

143-
# Option to control wmenu simulator build
144-
option(USE_SIMULATOR "Build the simulator version of the application" OFF)
145-
146150
if(USE_SIMULATOR)
147151
add_definitions(-DUSE_SIMULATOR)
148152
target_compile_definitions(lvgl PRIVATE USE_SIMULATOR)
@@ -181,6 +185,48 @@ endif()
181185

182186
target_include_directories(${PROJECT_NAME} PUBLIC "${PROJECT_BINARY_DIR}")
183187

188+
# Testing
189+
if(BUILD_TESTS)
190+
find_package(Catch2 REQUIRED)
191+
192+
# Test source files
193+
set(TEST_SOURCES
194+
tests/test_osd.cpp
195+
src/main.h
196+
src/main.cpp
197+
)
198+
find_package(spdlog REQUIRED)
199+
find_package(nlohmann_json 3 REQUIRED)
200+
find_package(yaml-cpp REQUIRED)
201+
202+
# Test executable
203+
add_executable(pixelpilot_tests ${LIB_SOURCE_FILES} ${TEST_SOURCES})
204+
target_include_directories(pixelpilot_tests PUBLIC "${PROJECT_BINARY_DIR}")
205+
# Define the TEST preprocessor directive
206+
target_compile_definitions(pixelpilot_tests PRIVATE TEST)
207+
target_link_libraries(pixelpilot_tests rockchip_mpp pthread drm m cairo spdlog::spdlog nlohmann_json::nlohmann_json yaml-cpp rt lvgl ${LIBGPIOD_LIBRARIES} ${PNG_LIBRARIES} Catch2::Catch2WithMain)
208+
209+
find_package(PkgConfig REQUIRED)
210+
pkg_search_module(GST REQUIRED
211+
gstreamer-1.0>=1.4
212+
gstreamer-app-1.0>=1.4
213+
)
214+
pkg_search_module(gstreamer REQUIRED IMPORTED_TARGET gstreamer-1.0>=1.4)
215+
pkg_search_module(gstreamer-app REQUIRED IMPORTED_TARGET gstreamer-app-1.0>=1.4)
216+
target_link_libraries(pixelpilot_tests PkgConfig::gstreamer PkgConfig::gstreamer-app)
217+
218+
target_link_options(pixelpilot_tests
219+
BEFORE PUBLIC -fsanitize=undefined PUBLIC -fsanitize=address
220+
)
221+
222+
# Optionally, establish testing options
223+
#enable_testing()
224+
#add_test(NAME PixelPilotTests COMMAND pixelpilot_tests)
225+
endif()
226+
227+
228+
# Installation
229+
184230
include(GNUInstallDirs)
185231
install(TARGETS ${PROJECT_NAME}
186232
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR}

Makefile

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ DEBIAN_CODENAME=bookworm
33
DEBIAN_HOST=https://cloud.debian.org/images/cloud/$(DEBIAN_CODENAME)
44
DEBIAN_RELEASE=latest
55
SKIP_SETUP=0
6-
# BUILD_TYPE=bin|debug
6+
# BUILD_TYPE=bin|debug|test
77
BUILD_TYPE=bin
88

99
ifeq ($(DEBIAN_CODENAME),bookworm)
@@ -48,6 +48,17 @@ qemu_build_deb:
4848
LC_ALL=en_US.UTF-8 sudo chroot $(OUTPUT) /usr/src/PixelPilot_rk/tools/container_build.sh --wipe-boot --pkg-version $(DEB_VERSION) --debian-codename $(DEBIAN_CODENAME) --build-type deb
4949
make umount
5050

51+
.PHONY: qemu_test
52+
qemu_test:
53+
mkdir -p $(OUTPUT)
54+
make mount
55+
56+
sudo rm $(OUTPUT)/etc/resolv.conf
57+
echo nameserver 1.1.1.1 | sudo tee -a $(OUTPUT)/etc/resolv.conf
58+
LC_ALL=en_US.UTF-8 sudo chroot $(OUTPUT) /usr/src/PixelPilot_rk/tools/container_build.sh --debian-codename $(DEBIAN_CODENAME) --build-type test $(if $(filter 1,$(SKIP_SETUP)),--skip-setup,)
59+
sudo chroot $(OUTPUT) /usr/src/PixelPilot_rk/tools/container_run_test.sh
60+
make umount
61+
5162
.PHONY: mount
5263
mount: disk.raw
5364
sudo mount `sudo losetup -P --show -f disk.raw`p1 $(OUTPUT)

src/input.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
#define INPUT_H
44

55
#include <stdint.h>
6-
#include "../../lvgl/lvgl.h"
7-
#include "../../lvgl/src/core/lv_global.h"
6+
#include "../lvgl/lvgl.h"
7+
#include "../lvgl/src/core/lv_global.h"
88

99
typedef enum {
1010
GSMENU_CONTROL_MODE_NAV = 0,

src/main.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -692,6 +692,7 @@ void printHelp() {
692692
}
693693

694694
// main
695+
#ifndef TEST
695696

696697
int main(int argc, char **argv)
697698
{
@@ -1151,3 +1152,5 @@ int main(int argc, char **argv)
11511152
restore_stdin();
11521153
return return_value;
11531154
}
1155+
1156+
#endif

src/osd.cpp

Lines changed: 86 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,11 @@ extern "C" {
4545
#include <nlohmann/json.hpp>
4646
#include "spdlog/spdlog.h"
4747
#include <fmt/ranges.h>
48-
#include "lvgl/lvgl.h"
48+
#include "../lvgl/lvgl.h"
49+
50+
#ifdef BUILD_TESTS
51+
#include <catch2/catch.hpp>
52+
#endif
4953

5054
#define WFB_LINK_LOST 1
5155
#define WFB_LINK_JAMMED 2
@@ -464,7 +468,7 @@ class Fact {
464468
return "(unknown)";
465469
}
466470

467-
std::string asVerboseString() {
471+
std::string asVerboseString() const {
468472
std::ostringstream oss;
469473
if (!isDefined()) {
470474
oss << "undef";
@@ -501,7 +505,7 @@ class Fact {
501505
void assertType(Type t) const {
502506
if (t != type) {
503507
spdlog::error("'{}': requested type of {}, but the actual type is {}",
504-
meta.getName(), typeName(t), typeName(type));
508+
asVerboseString(), typeName(t), typeName(type));
505509
assert(type == t);
506510
}
507511
}
@@ -762,7 +766,8 @@ class Widget {
762766
virtual void draw(cairo_t *cr) {};
763767

764768
virtual void setFact(uint idx, Fact fact) {
765-
args[idx] = fact;
769+
if (idx >= args.size()) throw std::out_of_range("setFact index out of range");
770+
args[idx] = fact;
766771
}
767772

768773
int x(cairo_t *cr) {
@@ -840,6 +845,10 @@ class TplTextWidget: public Widget {
840845
cairo_show_text(cr, msg->c_str());
841846
}
842847

848+
std::unique_ptr<std::string> render_tpl() {
849+
return render_tokens(_tokens, args);
850+
}
851+
843852
uint default_precision = 2;
844853

845854
protected:
@@ -865,10 +874,6 @@ class TplTextWidget: public Widget {
865874
: type(t), value(std::nullopt), precision(0) {}
866875
};
867876

868-
std::unique_ptr<std::string> render_tpl() {
869-
return render_tokens(_tokens, args);
870-
}
871-
872877
std::unique_ptr<std::string> render_tpl(const std::string& tpl, const std::vector<Fact>& facts) {
873878
auto tokens = tokenize(tpl);
874879
return render_tokens(tokens, facts);
@@ -922,7 +927,7 @@ class TplTextWidget: public Widget {
922927
if (match == "%%") {
923928
tokens.emplace_back(TokenType::Literal, "%");
924929
} else if (match[0] == '%') {
925-
if (match.size() == 2) { // Simple placeholder like %b, %i, %u, %s
930+
if (match.size() == 2) { // Simple placeholder like %b, %i, %u, %s, %f
926931
if (match[1] == 'b') {
927932
tokens.emplace_back(TokenType::Bool);
928933
} else if (match[1] == 'i' || match[1] == 'd') {
@@ -931,8 +936,10 @@ class TplTextWidget: public Widget {
931936
tokens.emplace_back(TokenType::Uint);
932937
} else if (match[1] == 's') {
933938
tokens.emplace_back(TokenType::String);
939+
} else if (match[1] == 'f') {
940+
tokens.emplace_back(TokenType::Float, default_precision);
934941
}
935-
} else if (match.back() == 'f') { // Float placeholder
942+
} else if (match.back() == 'f') { // Float placeholder with precision
936943
uint precision = 0;
937944
if (match.size() > 2 && match[1] == '.') {
938945
precision = std::stoi(match.substr(2, match.size() - 3)); // Extract precision
@@ -1864,8 +1871,6 @@ void modeset_paint_buffer(struct modeset_buf *buf, Osd *osd) {
18641871
unsigned int j,k,off;
18651872
cairo_t* cr;
18661873
cairo_surface_t *surface;
1867-
char msg[80];
1868-
memset(msg, 0x00, sizeof(msg));
18691874

18701875
int osd_x = buf->width - 300;
18711876
surface = cairo_image_surface_create_for_data(buf->map, CAIRO_FORMAT_ARGB32, buf->width, buf->height, buf->stride);
@@ -2180,3 +2185,72 @@ void osd_publish_str_fact(char const *name, osd_tag *tags, int n_tags, const cha
21802185
#ifdef __cplusplus
21812186
}
21822187
#endif
2188+
2189+
2190+
//
2191+
// Code below is only for unit-tests!
2192+
//
2193+
#ifdef TEST
2194+
2195+
TestExpressionTree::TestExpressionTree() {
2196+
tree = new ExpressionTree();
2197+
}
2198+
2199+
TestExpressionTree::TestExpressionTree(const std::string& expression) {
2200+
tree = new ExpressionTree(expression);
2201+
}
2202+
2203+
TestExpressionTree::~TestExpressionTree() {
2204+
delete tree;
2205+
}
2206+
2207+
std::vector<std::string> TestExpressionTree::tokenize(const std::string& input) {
2208+
return tree->tokenize(input);
2209+
}
2210+
2211+
void TestExpressionTree::parse(const std::string &expression) {
2212+
tree->parse(expression);
2213+
}
2214+
2215+
double TestExpressionTree::evaluate(double xValue) {
2216+
return tree->evaluate(xValue);
2217+
}
2218+
2219+
2220+
2221+
TestTplTextWidget::TestTplTextWidget(int pos_x, int pos_y, std::string tpl, uint n_args) {
2222+
widget = new TplTextWidget(pos_x, pos_y, tpl, n_args);
2223+
}
2224+
TestTplTextWidget::~TestTplTextWidget() {
2225+
delete widget;
2226+
}
2227+
void TestTplTextWidget::setBoolFact(uint idx, bool v) {
2228+
Fact fact = Fact(FactMeta("bool"), v);
2229+
widget->setFact(idx, fact);
2230+
};
2231+
void TestTplTextWidget::setLongFact(uint idx, long v) {
2232+
Fact fact = Fact(FactMeta("long"), v);
2233+
widget->setFact(idx, fact);
2234+
};
2235+
void TestTplTextWidget::setUlongFact(uint idx, ulong v) {
2236+
Fact fact = Fact(FactMeta("ulong"), v);
2237+
widget->setFact(idx, fact);
2238+
};
2239+
void TestTplTextWidget::setDoubleFact(uint idx, double v) {
2240+
Fact fact = Fact(FactMeta("double"), v);
2241+
widget->setFact(idx, fact);
2242+
};
2243+
void TestTplTextWidget::setStringFact(uint idx, std::string v) {
2244+
Fact fact = Fact(FactMeta("string"), v);
2245+
widget->setFact(idx, fact);
2246+
};
2247+
2248+
void TestTplTextWidget::draw(void *cr) {
2249+
widget->draw((cairo_t *) cr);
2250+
}
2251+
2252+
std::unique_ptr<std::string> TestTplTextWidget::render_tpl() {
2253+
return widget->render_tpl();
2254+
}
2255+
2256+
#endif

0 commit comments

Comments
 (0)