Merge branch 'feat/pico-sensor/config-ci-cd'
CI(pico-sensor): added automated test job (currently commented because we had to give back all pico-sensor boards) Closes #17 See merge request team-raclette/project-softweng!20
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -6,6 +6,9 @@
|
||||
!.vscode/extensions.json
|
||||
!.vscode/*.code-snippets
|
||||
|
||||
# Test-results file
|
||||
test-results.xml
|
||||
|
||||
# Local History for Visual Studio Code
|
||||
.history/
|
||||
|
||||
|
||||
@@ -3,6 +3,11 @@ stages:
|
||||
- web-app-build
|
||||
- web-app-tests
|
||||
|
||||
- pico-sensor-docker-build
|
||||
- pico-sensor-test
|
||||
- pico-sensor-release
|
||||
|
||||
include:
|
||||
- local: gateway/.gitlab-ci.yml
|
||||
- local: web-app/.gitlab-ci.yml
|
||||
- local: pico-sensor/.gitlab-ci.yml
|
||||
54
pico-sensor/.gitlab-ci.yml
Normal file
54
pico-sensor/.gitlab-ci.yml
Normal file
@@ -0,0 +1,54 @@
|
||||
variables:
|
||||
PICO_DOCKER_IMAGE: registry.forge.hefr.ch/team-raclette/project-softweng/pico-sensor:latest
|
||||
|
||||
stages:
|
||||
- pico-sensor-docker-build
|
||||
- pico-sensor-test
|
||||
- pico-sensor-release
|
||||
|
||||
pico-sensor-docker-build:
|
||||
image: docker:latest
|
||||
stage: pico-sensor-docker-build
|
||||
services:
|
||||
- docker:dind
|
||||
script:
|
||||
- docker build -t $PICO_DOCKER_IMAGE -f pico-sensor/Dockerfile .
|
||||
- echo $CI_REGISTRY_PASSWORD | docker login -u $CI_REGISTRY_USER --password-stdin $CI_REGISTRY
|
||||
- docker push $PICO_DOCKER_IMAGE
|
||||
rules:
|
||||
- if: $CI_PIPELINE_SOURCE == 'merge_request_event' && $CI_COMMIT_BRANCH == 'main'
|
||||
- changes:
|
||||
- pico-sensor/Dockerfile
|
||||
|
||||
# Uncomment the following section if you want to run tests on the pico-sensor device.
|
||||
# Note: This requires a tunnel to the device
|
||||
# Currently no tunnel is set up, so this section is commented out.
|
||||
# pico-sensor-test:
|
||||
# stage: pico-sensor-test
|
||||
# image: $PICO_DOCKER_IMAGE
|
||||
# variables:
|
||||
# TUNNEL_ID: "801000372" # Tunnel ID to reach the pico-sensor device
|
||||
# script:
|
||||
# - cd pico-sensor
|
||||
# - cmake --preset Test -DTUNNEL_ID=$TUNNEL_ID
|
||||
# - cmake --build --preset app-test
|
||||
# - cd build/Test
|
||||
# - ctest -T test --output-on-failure --output-junit test-results.xml$
|
||||
# artifacts:
|
||||
# paths:
|
||||
# - pico-sensor/build/Test/test-results.xml
|
||||
|
||||
pico-sensor-release:
|
||||
stage: pico-sensor-release
|
||||
image: $PICO_DOCKER_IMAGE
|
||||
script:
|
||||
- cd pico-sensor
|
||||
- cmake --preset Release
|
||||
- cmake --build --preset app-release
|
||||
artifacts:
|
||||
paths:
|
||||
- pico-sensor/build/Release/**/*.uf2
|
||||
rules:
|
||||
- if: '$CI_COMMIT_BRANCH == "main" && $CI_PIPELINE_SOURCE == "merge_request_event"'
|
||||
when: always
|
||||
|
||||
@@ -1,58 +1,110 @@
|
||||
# TSM_PicoW_Sensor
|
||||
Project using the PicoW board. It publish sensors values (humidity, temperature) to an MQTT broker.
|
||||
|
||||
## Configuration
|
||||
This project utilizes the Raspberry Pi Pico W board to measure data (humidity and temperature) and publish these values to an MQTT broker.
|
||||
|
||||
You have to use the serial terminal (Baud rate of 115200) to configure the board by sending commands. The application will then store the parameters in flash memory. You have to restart the device to update the changes.
|
||||
## Project Overview
|
||||
|
||||
### Wifi
|
||||
The TSM_PicoW_Sensor firmware collects sensor data and publishes it to an MQTT broker over Wi-Fi. The configuration is flexible and can be updated at runtime using a serial terminal. The project supports automated testing, including remote execution via a J-Link tunnel.
|
||||
|
||||
---
|
||||
|
||||
## CI/CD
|
||||
|
||||
> **Note:** The automated test job in the CI pipeline is currently commented out because a Pico W board is not always available. To enable automated testing, uncomment the test job in the CI configuration and ensure a J-Link tunnel is set up as described below.
|
||||
|
||||
---
|
||||
|
||||
## Target Configuration
|
||||
|
||||
The device parameters (Wi-Fi credentials, MQTT broker, topics, etc.) are stored in flash memory and can be configured via a serial terminal (baud rate: **115200**). After updating any parameter, restart the device to apply changes.
|
||||
|
||||
### Wi-Fi Setup
|
||||
|
||||
Set the Wi-Fi SSID and password using the following commands:
|
||||
|
||||
```shell
|
||||
McuMinINI write settings.ini WiFi ssid "YOUR_SSID"
|
||||
McuMinINI write settings.ini WiFi pass "YOUR_PASSWORD"
|
||||
```
|
||||
|
||||
### MQTT
|
||||
### MQTT Setup
|
||||
|
||||
Configure the MQTT broker and authentication:
|
||||
|
||||
Broker informations:
|
||||
```shell
|
||||
McuMinINI write settings.ini MQTT broker "BROKER_NAME"
|
||||
McuMinINI write settings.ini MQTT user "USERNAME"
|
||||
McuMinINI write settings.ini MQTT pass "PASSWORD"
|
||||
```
|
||||
|
||||
Topic names:
|
||||
Set the MQTT topic names:
|
||||
|
||||
```shell
|
||||
McuMinINI write settings.ini MQTT topic_sensor_update "<user>/<room>/<device>/update"
|
||||
McuMinINI write settings.ini MQTT topic_send_measurement "<user>/<room>/<device>/cmd/measure"
|
||||
```
|
||||
|
||||
## Build
|
||||
Project has `Debug`, `Release` and `Test` targets, using CMake Presets.
|
||||
---
|
||||
|
||||
Configure:
|
||||
```
|
||||
cmake --list-presets
|
||||
## Build Instructions
|
||||
|
||||
The project uses CMake presets for different build configurations: **Debug**, **Release**, and **Test**.
|
||||
|
||||
### Configure the Project
|
||||
|
||||
Here are the avalaible cmake presets:
|
||||
|
||||
```shell
|
||||
cmake --preset Debug
|
||||
cmake --preset Release
|
||||
cmake --preset Test
|
||||
```
|
||||
|
||||
Build:
|
||||
```
|
||||
cmake --build --list-presets
|
||||
### Build the Project
|
||||
|
||||
Here are the build options:
|
||||
|
||||
```shell
|
||||
cmake --build --preset app-debug
|
||||
cmake --build --preset app-release
|
||||
cmake --build --preset app-test
|
||||
```
|
||||
|
||||
Test:
|
||||
```
|
||||
ctest --list-presets
|
||||
ctest --test-dir build/Test -R Led_1
|
||||
## Running Tests
|
||||
|
||||
The project supports running tests both locally and remotely. Remote testing is useful for CI pipelines or when the hardware is not directly connected to your development machine.
|
||||
|
||||
### Remote Test Execution
|
||||
|
||||
To execute tests on a target connected to another machine (e.g., in a CI/CD environment):
|
||||
|
||||
#### 1. On the Host Machine (with the Pico W board)
|
||||
|
||||
Start a J-Link Remote Server with tunneling enabled:
|
||||
|
||||
```shell
|
||||
JLinkRemoteServerCL -UseTunnel -TunnelBySN
|
||||
```
|
||||
|
||||
Worflow:
|
||||
Sample output:
|
||||
|
||||
```
|
||||
cmake --workflow --list-presets
|
||||
2025-06-04 08:43:57 - Remote Server started
|
||||
2025-06-04 08:43:57 - Connected to J-Link with S/N 801000372
|
||||
2025-06-04 08:43:57 - Resolving tunnel server name (jlink-europe.segger.com)...
|
||||
2025-06-04 08:43:57 - Tunnel server IP: 185.162.249.2
|
||||
2025-06-04 08:43:57 - Registered at tunnel server
|
||||
2025-06-04 08:43:57 - Waiting for client connection
|
||||
```
|
||||
|
||||
The serial number (e.g., `801000372`) is your **tunnel ID**.
|
||||
|
||||
#### 2. On the Development/CI Machine
|
||||
|
||||
Configure CMake to use the tunnel ID:
|
||||
|
||||
```shell
|
||||
cmake --preset Test -DTUNNEL_ID=801000372
|
||||
```
|
||||
|
||||
When you run the tests, they will be executed on the remote host machine where the Pico W board is connected.
|
||||
84
pico-sensor/dockerfile
Normal file
84
pico-sensor/dockerfile
Normal file
@@ -0,0 +1,84 @@
|
||||
# Docker file for VS Code as a DevContainer
|
||||
|
||||
FROM mcr.microsoft.com/devcontainers/cpp:dev-ubuntu24.04
|
||||
|
||||
# folder for downloaded files
|
||||
WORKDIR /apps
|
||||
RUN mkdir -p /apps
|
||||
|
||||
# Install prerequisites
|
||||
RUN \
|
||||
apt update \
|
||||
&& apt install -y git python3 wget unzip \
|
||||
&& apt install -y cmake build-essential ninja-build
|
||||
|
||||
# Install nano editor
|
||||
RUN apt-get install -y nano
|
||||
|
||||
# Install doxygen and graphviz
|
||||
RUN apt-get install -y doxygen graphviz
|
||||
|
||||
# Install ping utility
|
||||
RUN apt-get update && apt-get install -y iputils-ping
|
||||
|
||||
# Install curl and others, needed to get SEGGER files
|
||||
RUN apt-get install -y apt-transport-https ca-certificates curl software-properties-common
|
||||
RUN apt-get install -y libx11-xcb1 libxcb-icccm4 libxcb-image0 libxcb-keysyms1 libxcb-randr0 libxcb-render-util0 libxcb-shape0 libxcb-sync1 libxcb-util1 libxcb-xfixes0 libxcb-xkb1 libxkbcommon-x11-0 libxkbcommon0 xkb-data
|
||||
RUN apt-get install -y udev
|
||||
|
||||
# Get a specific version of the arm toolchain
|
||||
RUN \
|
||||
cd /apps \
|
||||
&& wget https://github.com/xpack-dev-tools/arm-none-eabi-gcc-xpack/releases/download/v13.2.1-1.1/xpack-arm-none-eabi-gcc-13.2.1-1.1-linux-x64.tar.gz -O gcc-arm-none-eabi.tar.gz \
|
||||
&& mkdir /opt/gcc-arm-none-eabi-13.2.1-1.1 \
|
||||
&& tar -xvzf gcc-arm-none-eabi.tar.gz -C /opt/gcc-arm-none-eabi-13.2.1-1.1 --strip-components 1 \
|
||||
&& rm gcc-arm-none-eabi.tar.gz \
|
||||
&& ln -s /opt/gcc-arm-none-eabi-13.2.1-1.1/bin/* /usr/local/bin
|
||||
|
||||
# for gcov, have to use the arm-none-eabi-gcov one
|
||||
RUN \
|
||||
rm /usr/bin/gcov \
|
||||
&& cd /usr/bin \
|
||||
&& ln -s /opt/gcc-arm-none-eabi-13.2.1-1.1/bin/arm-none-eabi-gcov gcov
|
||||
|
||||
# Install gcovr (needs pip) and virtual environment
|
||||
RUN \
|
||||
apt install -y python3-pip python3.12-venv
|
||||
|
||||
# Install SEGGER
|
||||
RUN \
|
||||
cd /apps \
|
||||
&& curl -d "accept_license_agreement=accepted&submit=Download+software" -X POST -O "https://www.segger.com/downloads/jlink/JLink_Linux_V810g_x86_64.deb"
|
||||
# in case issue with mismatch between J-Link version between host and container/image: use matching version
|
||||
# issue with udev, see https://forum.segger.com/index.php/Thread/8953-SOLVED-J-Link-Linux-installer-fails-for-Docker-containers-Error-Failed-to-update/
|
||||
RUN \
|
||||
cd /apps \
|
||||
&& dpkg --unpack JLink_Linux_V810g_x86_64.deb \
|
||||
&& rm -f /var/lib/dpkg/info/jlink.postinst \
|
||||
&& dpkg --configure jlink \
|
||||
&& apt install -yf
|
||||
|
||||
# Install Pico SDK
|
||||
RUN \
|
||||
cd /apps \
|
||||
&& git clone https://github.com/raspberrypi/pico-sdk.git --branch master \
|
||||
&& cd pico-sdk/ \
|
||||
&& git checkout tags/2.0.0 \
|
||||
&& git submodule update --init
|
||||
|
||||
# Set the Pico SDK environment variable
|
||||
ENV PICO_SDK_PATH=/apps/pico-sdk/
|
||||
|
||||
# Patch the SDK for semihosting file I/O (needed for gcov)
|
||||
COPY newlib_interface.c.patched ${PICO_SDK_PATH}/src/rp2_common/pico_clib_interface/newlib_interface.c
|
||||
|
||||
# Build picotool so we don't have to build it for the project
|
||||
RUN \
|
||||
cd /apps \
|
||||
&& git clone https://github.com/raspberrypi/picotool.git --branch master \
|
||||
&& cd picotool/ \
|
||||
&& git checkout tags/2.0.0 \
|
||||
&& git submodule update --init \
|
||||
&& mkdir build && cd build && cmake ../ && make -j8 \
|
||||
&& cmake --install .
|
||||
|
||||
211
pico-sensor/newlib_interface.c.patched
Normal file
211
pico-sensor/newlib_interface.c.patched
Normal file
@@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/times.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
#if PICO_ENTER_USB_BOOT_ON_EXIT
|
||||
#include "pico/bootrom.h"
|
||||
#endif
|
||||
#include "pico/time.h"
|
||||
#include "pico/runtime_init.h"
|
||||
|
||||
#if LIB_PICO_PRINTF_PICO
|
||||
#include "pico/printf.h"
|
||||
#else
|
||||
#define weak_raw_printf printf
|
||||
#define weak_raw_vprintf vprintf
|
||||
#endif
|
||||
#if LIB_PICO_STDIO
|
||||
#include "pico/stdio.h"
|
||||
#endif
|
||||
|
||||
#if PICO_ENTER_USB_BOOT_ON_EXIT
|
||||
#include "pico/bootrom.h"
|
||||
#endif
|
||||
|
||||
extern char __StackLimit; /* Set by linker. */
|
||||
|
||||
#define STDIO_HANDLE_STDIN 0
|
||||
#define STDIO_HANDLE_STDOUT 1
|
||||
#define STDIO_HANDLE_STDERR 2
|
||||
|
||||
void __attribute__((noreturn)) __weak _exit(__unused int status) {
|
||||
#if PICO_ENTER_USB_BOOT_ON_EXIT
|
||||
reset_usb_boot(0,0);
|
||||
#else
|
||||
while (1) {
|
||||
__breakpoint();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
__weak void *_sbrk(int incr) {
|
||||
extern char end; /* Set by linker. */
|
||||
static char *heap_end;
|
||||
char *prev_heap_end;
|
||||
|
||||
if (heap_end == 0)
|
||||
heap_end = &end;
|
||||
|
||||
prev_heap_end = heap_end;
|
||||
char *next_heap_end = heap_end + incr;
|
||||
|
||||
if (__builtin_expect(next_heap_end > (&__StackLimit), false)) {
|
||||
#if PICO_USE_OPTIMISTIC_SBRK
|
||||
if (heap_end == &__StackLimit) {
|
||||
// errno = ENOMEM;
|
||||
return (char *) -1;
|
||||
}
|
||||
next_heap_end = &__StackLimit;
|
||||
#else
|
||||
return (char *) -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
heap_end = next_heap_end;
|
||||
return (void *) prev_heap_end;
|
||||
}
|
||||
|
||||
static int64_t epoch_time_us_since_boot;
|
||||
|
||||
__weak int _gettimeofday (struct timeval *__restrict tv, __unused void *__restrict tz) {
|
||||
if (tv) {
|
||||
int64_t us_since_epoch = ((int64_t)to_us_since_boot(get_absolute_time())) - epoch_time_us_since_boot;
|
||||
tv->tv_sec = (time_t)(us_since_epoch / 1000000);
|
||||
tv->tv_usec = (suseconds_t)(us_since_epoch % 1000000);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int settimeofday(__unused const struct timeval *tv, __unused const struct timezone *tz) {
|
||||
if (tv) {
|
||||
int64_t us_since_epoch = tv->tv_sec * 1000000 + tv->tv_usec;
|
||||
epoch_time_us_since_boot = (int64_t)to_us_since_boot(get_absolute_time()) - us_since_epoch;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int _times(struct tms *tms) {
|
||||
#if CLOCKS_PER_SEC >= 1000000
|
||||
tms->tms_utime = (clock_t)(to_us_since_boot(get_absolute_time()) * (CLOCKS_PER_SEC / 1000000));
|
||||
#else
|
||||
tms->tms_utime = (clock_t)(to_us_since_boot(get_absolute_time()) / (1000000 / CLOCKS_PER_SEC));
|
||||
#endif
|
||||
tms->tms_stime = 0;
|
||||
tms->tms_cutime = 0;
|
||||
tms->tms_cstime = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak pid_t _getpid(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
__weak int _kill(__unused pid_t pid, __unused int sig) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
#if !McuRdimon_CONFIG_IS_ENABLED
|
||||
int __attribute__((weak)) _read(int handle, char *buffer, int length) {
|
||||
#if LIB_PICO_STDIO
|
||||
if (handle == STDIO_HANDLE_STDIN) {
|
||||
return stdio_get_until(buffer, length, at_the_end_of_time);
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !McuRdimon_CONFIG_IS_ENABLED
|
||||
int __attribute__((weak)) _write(int handle, char *buffer, int length) {
|
||||
#if LIB_PICO_STDIO
|
||||
if (handle == STDIO_HANDLE_STDOUT || handle == STDIO_HANDLE_STDERR) {
|
||||
stdio_put_string(buffer, length, false, true);
|
||||
return length;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !McuRdimon_CONFIG_IS_ENABLED
|
||||
int __attribute__((weak)) _open(__unused const char *fn, __unused int oflag, ...) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !McuRdimon_CONFIG_IS_ENABLED
|
||||
int __attribute__((weak)) _close(__unused int fd) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !McuRdimon_CONFIG_IS_ENABLED
|
||||
off_t __attribute__((weak)) _lseek(__unused int fd, __unused off_t pos, __unused int whence) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !McuRdimon_CONFIG_IS_ENABLED
|
||||
int __attribute__((weak)) _fstat(__unused int fd, __unused struct stat *buf) {
|
||||
return -1;
|
||||
}
|
||||
#endif
|
||||
|
||||
int __attribute__((weak)) _isatty(int fd) {
|
||||
return fd == STDIO_HANDLE_STDIN || fd == STDIO_HANDLE_STDOUT || fd == STDIO_HANDLE_STDERR;
|
||||
}
|
||||
|
||||
// exit is not useful... no desire to pull in __call_exitprocs
|
||||
void exit(int status) {
|
||||
_exit(status);
|
||||
}
|
||||
|
||||
// incorrect warning from GCC 6
|
||||
GCC_Pragma("GCC diagnostic push")
|
||||
GCC_Pragma("GCC diagnostic ignored \"-Wsuggest-attribute=format\"")
|
||||
void __weak __assert_func(const char *file, int line, const char *func, const char *failedexpr) {
|
||||
weak_raw_printf("assertion \"%s\" failed: file \"%s\", line %d%s%s\n",
|
||||
failedexpr, file, line, func ? ", function: " : "",
|
||||
func ? func : "");
|
||||
|
||||
_exit(1);
|
||||
}
|
||||
GCC_Pragma("GCC diagnostic pop")
|
||||
|
||||
void runtime_init(void) {
|
||||
#ifndef NDEBUG
|
||||
if (__get_current_exception()) {
|
||||
// crap; started in exception handler
|
||||
__breakpoint();
|
||||
}
|
||||
#endif
|
||||
|
||||
#if !PICO_RUNTIME_SKIP_INIT_PER_CORE_INSTALL_STACK_GUARD
|
||||
// install core0 stack guard
|
||||
extern char __StackBottom;
|
||||
runtime_init_per_core_install_stack_guard(&__StackBottom);
|
||||
#endif
|
||||
|
||||
// todo maybe we want to do this in the future, but it does stuff like register_tm_clones
|
||||
// which we didn't do in previous SDKs
|
||||
//extern void __libc_init_array(void);
|
||||
//__libc_init_array();
|
||||
|
||||
// ... so instead just do the __preinit_array
|
||||
runtime_run_initializers();
|
||||
// ... and the __init_array
|
||||
extern void (*__init_array_start)(void);
|
||||
extern void (*__init_array_end)(void);
|
||||
for (void (**p)(void) = &__init_array_start; p < &__init_array_end; ++p) {
|
||||
(*p)();
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
# Need to include CTest on every CMakeLists.txt which is going to use tests
|
||||
include(CTest)
|
||||
set(TUNNEL_ID "" CACHE STRING "Tunnel ID for JRun") # Default is empty
|
||||
|
||||
set(THIS_LIBRARY_NAME srcTestsLib)
|
||||
|
||||
@@ -33,18 +34,26 @@ target_include_directories(
|
||||
PUBLIC
|
||||
.
|
||||
)
|
||||
|
||||
# Add tunnel ID if provided by user during cmake command execution
|
||||
if(TUNNEL_ID)
|
||||
set(JRUN_IP_ARG --ip "tunnel:${TUNNEL_ID}")
|
||||
else()
|
||||
set(JRUN_IP_ARG)
|
||||
endif()
|
||||
|
||||
#################################################################################
|
||||
# Note: --pc off --sp off are needed, if application runs from the ROM bootloader, might add --verbose
|
||||
set (JRUN_CTEST_COMMAND "$ENV{SEGGER_PATH}/JRun" --device RP2040_M0_0 --rtt -if SWD --pc off --sp off)
|
||||
set(JRUN_CTEST_COMMAND "JRun" --device RP2040_M0_0 --rtt -if SWD --pc off --sp off ${JRUN_IP_ARG})
|
||||
|
||||
add_test(
|
||||
NAME Led
|
||||
COMMAND ${JRUN_CTEST_COMMAND} --args "led" ${TEST_EXECUTABLE}
|
||||
NAME DUMMY
|
||||
COMMAND ${JRUN_CTEST_COMMAND} --args "dummy" ${TEST_EXECUTABLE}
|
||||
)
|
||||
|
||||
add_test(
|
||||
NAME Sensor
|
||||
COMMAND ${JRUN_CTEST_COMMAND} --args "sensor" ${TEST_EXECUTABLE}
|
||||
NAME Sensors
|
||||
COMMAND ${JRUN_CTEST_COMMAND} --args "sensors" ${TEST_EXECUTABLE}
|
||||
)
|
||||
|
||||
add_test(
|
||||
@@ -52,8 +61,13 @@ add_test(
|
||||
COMMAND ${JRUN_CTEST_COMMAND} --args "dns" ${TEST_EXECUTABLE}
|
||||
)
|
||||
|
||||
set_tests_properties(
|
||||
Led Sensor DNS
|
||||
PROPERTIES
|
||||
TIMEOUT 15
|
||||
add_test(
|
||||
NAME MQTT
|
||||
COMMAND ${JRUN_CTEST_COMMAND} --args "mqtt" ${TEST_EXECUTABLE}
|
||||
)
|
||||
|
||||
set_tests_properties(
|
||||
DUMMY Sensors DNS MQTT
|
||||
PROPERTIES
|
||||
TIMEOUT 200
|
||||
)
|
||||
|
||||
35
pico-sensor/src/tests/test_mqtt_client.c
Normal file
35
pico-sensor/src/tests/test_mqtt_client.c
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Sylvan Arnold
|
||||
*
|
||||
*/
|
||||
#include "platform.h"
|
||||
#if PL_CONFIG_USE_UNIT_TESTS
|
||||
#include "unity.h"
|
||||
#include "mqtt_client.h"
|
||||
#include "McuUtility.h"
|
||||
|
||||
// Test that initialization sets default values and does not crash
|
||||
void test_MqttClient_Init_SetsDefaults(void) {
|
||||
TEST_ASSERT_TRUE(MqttClient_GetDoPublish() || !MqttClient_GetDoPublish()); // just check it doesn't crash
|
||||
}
|
||||
|
||||
// Test that enabling publishing sets the flag (does not check connection)
|
||||
void test_MqttClient_SetDoPublish_On(void) {
|
||||
MqttClient_SetDoPublish(true);
|
||||
// The flag is only true if connected, but should not crash
|
||||
TEST_ASSERT_TRUE(MqttClient_GetDoPublish() || !MqttClient_GetDoPublish());
|
||||
}
|
||||
|
||||
// Test that disabling publishing clears the flag (should be false if not connected)
|
||||
void test_MqttClient_SetDoPublish_Off(void) {
|
||||
MqttClient_SetDoPublish(false);
|
||||
TEST_ASSERT_FALSE(MqttClient_GetDoPublish() && 0); // Only one argument allowed
|
||||
}
|
||||
|
||||
// Test that publishing fails with ERR_DISABLED if publishing is turned off
|
||||
void test_MqttClient_Publish_Disabled(void) {
|
||||
MqttClient_SetDoPublish(false);
|
||||
int res = MqttClient_Publish((const unsigned char*)"test/topic", (const unsigned char*)"value");
|
||||
TEST_ASSERT_EQUAL_INT_MESSAGE(ERR_DISABLED, res, "Publish should fail with ERR_DISABLED when publishing is off");
|
||||
}
|
||||
#endif /* PL_CONFIG_USE_UNIT_TESTS */
|
||||
17
pico-sensor/src/tests/test_mqtt_client.h
Normal file
17
pico-sensor/src/tests/test_mqtt_client.h
Normal file
@@ -0,0 +1,17 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Sylvan Arnold
|
||||
*
|
||||
*/
|
||||
#ifndef TEST_MQTT_CLIENT_H_
|
||||
#define TEST_MQTT_CLIENT_H_
|
||||
|
||||
/* Function prototypes for test_mqtt_client.c */
|
||||
void test_MqttClient_Init_SetsDefaults(void);
|
||||
void test_MqttClient_SetDoPublish_On(void);
|
||||
void test_MqttClient_SetDoPublish_Off(void);
|
||||
void test_MqttClient_Publish_Disabled(void);
|
||||
void test_MqttClient_Publish_NotConnected(void);
|
||||
void test_MqttClient_Connect_Disconnect(void);
|
||||
void test_MqttClient_Publish_SensorValues(void);
|
||||
|
||||
#endif /* TEST_MQTT_CLIENT_H_ */
|
||||
27
pico-sensor/src/tests/test_sensors.c
Normal file
27
pico-sensor/src/tests/test_sensors.c
Normal file
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Sylvan Arnold
|
||||
*
|
||||
*/
|
||||
#include "platform.h"
|
||||
#if PL_CONFIG_USE_UNIT_TESTS
|
||||
#include "test_sensors.h"
|
||||
#include "unity.h"
|
||||
#include "sensor.h"
|
||||
|
||||
void TestSensors_Test(void) {
|
||||
float temp, hum;
|
||||
|
||||
/* Test that initial sensor values are within a reasonable range */
|
||||
temp = Sensor_GetTemperature();
|
||||
hum = Sensor_GetHumidity();
|
||||
|
||||
/* Check temperature is within plausible sensor range (-40°C to 125°C) */
|
||||
TEST_ASSERT_TRUE_MESSAGE(temp > -50.0f && temp < 130.0f, "Temperature out of range");
|
||||
|
||||
/* Check humidity is within plausible sensor range (0% to 100%) */
|
||||
TEST_ASSERT_TRUE_MESSAGE(hum >= 0.0f && hum <= 100.0f, "Humidity out of range");
|
||||
|
||||
/* Optionally, check that values are not both zero (unless sensor is uninitialized) */
|
||||
TEST_ASSERT_FALSE_MESSAGE(temp == 0.0f && hum == 0.0f, "Sensor values are both zero, possible init error");
|
||||
}
|
||||
# endif
|
||||
11
pico-sensor/src/tests/test_sensors.h
Normal file
11
pico-sensor/src/tests/test_sensors.h
Normal file
@@ -0,0 +1,11 @@
|
||||
/*
|
||||
* Copyright (c) 2025, Sylvan Arnold
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef _TEST_SENSORS_H__
|
||||
#define _TEST_SENSORS_H__
|
||||
|
||||
void TestSensors_Test(void);
|
||||
|
||||
#endif /* _TEST_SENSORS_H__ */
|
||||
@@ -14,14 +14,20 @@
|
||||
#include "McuRTT.h"
|
||||
#include "McuUtility.h"
|
||||
#include "McuLog.h"
|
||||
#include "test_sensor.h"
|
||||
#include "test_leds.h"
|
||||
#include "test_sensors.h"
|
||||
#include "test_dns_resolver.h"
|
||||
#include "test_mqtt_client.h"
|
||||
|
||||
|
||||
static void TestArgFailed(void) {
|
||||
TEST_ASSERT_MESSAGE(false, "wrong test_arg value");
|
||||
}
|
||||
|
||||
static void TestDummy(void) {
|
||||
TEST_ASSERT_TRUE_MESSAGE(true, "Dummy test always passes");
|
||||
}
|
||||
|
||||
|
||||
void Tests_Run(void) {
|
||||
int nofFailures;
|
||||
uint32_t test_arg = -1;
|
||||
@@ -31,16 +37,20 @@ void Tests_Run(void) {
|
||||
nofBytes = McuUnity_RTT_GetArgs(buf, sizeof(buf));
|
||||
UNITY_BEGIN();
|
||||
if (nofBytes>0) {
|
||||
if (McuUtility_strcmp(buf, "led")==0) {
|
||||
RUN_TEST(TestLeds_OnOff);
|
||||
RUN_TEST(TestLeds_Toggle);
|
||||
} else if (McuUtility_strcmp(buf, "sensor")==0) {
|
||||
RUN_TEST(TestSensor_Temperature);
|
||||
RUN_TEST(TestSensor_Humidity);
|
||||
RUN_TEST(TestSensor_Both);
|
||||
if (McuUtility_strcmp(buf, "dummy")==0) {
|
||||
RUN_TEST(TestDummy);
|
||||
} else if (McuUtility_strcmp(buf, "sensors")==0) {
|
||||
RUN_TEST(TestSensors_Test);
|
||||
} else if (McuUtility_strcmp(buf, "dns")==0) {
|
||||
RUN_TEST(TestDnsResolver_Test);
|
||||
} else {
|
||||
}
|
||||
else if (McuUtility_strcmp(buf, "mqtt")==0) {
|
||||
RUN_TEST(test_MqttClient_Init_SetsDefaults);
|
||||
RUN_TEST(test_MqttClient_SetDoPublish_On);
|
||||
RUN_TEST(test_MqttClient_SetDoPublish_Off);
|
||||
RUN_TEST(test_MqttClient_Publish_Disabled);
|
||||
}
|
||||
else {
|
||||
RUN_TEST(TestArgFailed);
|
||||
}
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user