Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

✅ Add unit testing framework #26948

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
ec14521
Add exec_tests_failing, to test unhappy paths
costas-basdekis Nov 1, 2020
0e946ff
Add ability to run unit tests
costas-basdekis Nov 3, 2020
b7c3462
Update tooling to work with modern Platform I/O and Marlin
sjasonsmith Apr 8, 2024
da97377
Refactor test mechanism to make tests more self-discovering and easie…
sjasonsmith Apr 8, 2024
9648cd8
Update READMEs
sjasonsmith Apr 8, 2024
e855bb8
Add to test-builds workflow
sjasonsmith Apr 9, 2024
ea5076d
Rename job
sjasonsmith Apr 9, 2024
41f0dcc
Restrict to bugfix-2.1.x branch, update comments.
sjasonsmith Apr 9, 2024
53ce692
whitespace, formatting, etc.
thinkyhead Apr 9, 2024
f3ad255
Split CI testing into two workflows
sjasonsmith Apr 9, 2024
903d5da
Use INI files inside test folders instead of CPP files
sjasonsmith Apr 9, 2024
8889e4f
Use config.ini
sjasonsmith Apr 9, 2024
e5ed773
Remove unused code
sjasonsmith Apr 9, 2024
efc9b54
parser.parse() does not accept const
sjasonsmith Apr 9, 2024
9484cb3
Revert unrelated changes
sjasonsmith Apr 9, 2024
68fac74
Update README to reflect config.ini change
sjasonsmith Apr 9, 2024
5514777
TEMPORARY changes to test github workflows
sjasonsmith Apr 9, 2024
28ec011
Revert unrelated changes
sjasonsmith Apr 9, 2024
791ddf3
Avoid main conflict with LINUX_HAL
sjasonsmith Apr 9, 2024
e0eee1a
Avoid SIGSEGV on timer cleanup
sjasonsmith Apr 9, 2024
3b6e647
Remove parse_line test
sjasonsmith Apr 9, 2024
c014c62
Revert "TEMPORARY changes to test github workflows"
sjasonsmith Apr 9, 2024
9e9a008
move YML
thinkyhead Apr 9, 2024
2767706
readme updates
thinkyhead Apr 9, 2024
6d90aab
rename
thinkyhead Apr 9, 2024
187e186
table format
thinkyhead Apr 9, 2024
6e7b10a
not force-pushed
thinkyhead Apr 9, 2024
55d836e
sort CI tests
thinkyhead Apr 9, 2024
e3da66e
test name from file
thinkyhead Apr 9, 2024
c0cbc71
not force-pushed
thinkyhead Apr 9, 2024
10c81cc
move string tests
thinkyhead Apr 9, 2024
296048d
tests-code => unit-test
thinkyhead Apr 9, 2024
fd351a9
split up string test
thinkyhead Apr 9, 2024
4118f36
fix docker "make" invocation
thinkyhead Apr 9, 2024
9d6f47d
string test
thinkyhead Apr 9, 2024
6a4be39
tweak readme
thinkyhead Apr 9, 2024
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
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#
# test-builds.yml
# ci-build-tests.yml
# Do test builds to catch compile errors
#

name: CI
name: CI - Build Tests

on:
pull_request:
Expand All @@ -27,7 +27,7 @@ on:

jobs:
test_builds:
name: Run All Tests
name: Build Test
if: github.repository == 'MarlinFirmware/Marlin'

runs-on: ubuntu-latest
Expand Down
73 changes: 73 additions & 0 deletions .github/workflows/ci-unit-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
#
# ci-unit-tests.yml
# Build and execute unit tests to catch functional issues in code
#

name: CI - Unit Tests

on:
pull_request:
branches:
- bugfix-2.1.x
# Cannot be enabled on 2.1.x until it contains the unit test framework
#- 2.1.x
paths-ignore:
- config/**
- data/**
- docs/**
- '**/*.md'
push:
branches:
- bugfix-2.1.x
# Cannot be enabled on 2.1.x until it contains the unit test framework
#- 2.1.x
paths-ignore:
- config/**
- data/**
- docs/**
- '**/*.md'

jobs:
# This runs all unit tests as a single job. While it should be possible to break this up into
# multiple jobs, they currently run quickly and finish long before the compilation tests.
run_unit_tests:
name: Unit Test
# These tests will only be able to run on the bugfix-2.1.x branch, until the next release
# pulls them into additional branches.
if: github.repository == 'MarlinFirmware/Marlin'

runs-on: ubuntu-latest

steps:
- name: Check out the PR
uses: actions/checkout@v4

- name: Cache pip
uses: actions/cache@v4
with:
path: ~/.cache/pip
key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements.txt') }}
restore-keys: |
${{ runner.os }}-pip-
- name: Cache PlatformIO
uses: actions/cache@v4
with:
path: ~/.platformio
key: ${{ runner.os }}-${{ hashFiles('**/lockfiles') }}

- name: Select Python 3.9
uses: actions/setup-python@v5
with:
python-version: '3.9'
architecture: 'x64'

- name: Install PlatformIO
run: |
pip install -U platformio
pio upgrade --dev
pio pkg update --global
- name: Run All Unit Tests
run: |
make unit-test-all-local
34 changes: 30 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,12 @@ help:
@echo "make tests-single-local-docker : Run a single test locally, using docker"
@echo "make tests-all-local : Run all tests locally"
@echo "make tests-all-local-docker : Run all tests locally, using docker"
@echo "make setup-local-docker : Build the local docker image"
# @echo "make unit-test-single-ci : Run a single code test from inside the CI"
# @echo "make unit-test-single-local : Run a single code test locally"
# @echo "make unit-test-single-local-docker : Run a single code test locally, using docker-compose"
@echo "make unit-test-all-local : Run all code tests locally"
@echo "make unit-test-all-local-docker : Run all code tests locally, using docker-compose"
@echo "make setup-local-docker : Setup local docker-compose"
@echo ""
@echo "Options for testing:"
@echo " TEST_TARGET Set when running tests-single-*, to select the"
Expand Down Expand Up @@ -43,7 +48,7 @@ tests-single-local:
tests-single-local-docker:
@if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET=<your-module> or use make tests-all-local-docker" ; return 1; fi
@if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi
$(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) $(MAKE) tests-single-local TEST_TARGET=$(TEST_TARGET) VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) ONLY_TEST="$(ONLY_TEST)"
$(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make tests-single-local TEST_TARGET=$(TEST_TARGET) VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD) ONLY_TEST="$(ONLY_TEST)"

tests-all-local:
export PATH="./buildroot/bin/:./buildroot/tests/:${PATH}" \
Expand All @@ -52,10 +57,31 @@ tests-all-local:

tests-all-local-docker:
@if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi
$(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) $(MAKE) tests-all-local VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD)
$(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make tests-all-local VERBOSE_PLATFORMIO=$(VERBOSE_PLATFORMIO) GIT_RESET_HARD=$(GIT_RESET_HARD)

#unit-test-single-ci:
# export GIT_RESET_HARD=true
# $(MAKE) unit-test-single-local TEST_TARGET=$(TEST_TARGET)

# TODO: How can we limit tests with ONLY_TEST with platformio?
#unit-test-single-local:
# @if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET=<your-module> or use make unit-test-all-local" ; return 1; fi
# platformio run -t marlin_$(TEST_TARGET)

#unit-test-single-local-docker:
# @if ! test -n "$(TEST_TARGET)" ; then echo "***ERROR*** Set TEST_TARGET=<your-module> or use make unit-test-all-local-docker" ; return 1; fi
# @if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi
# $(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make unit-test-single-local TEST_TARGET=$(TEST_TARGET) ONLY_TEST="$(ONLY_TEST)"

unit-test-all-local:
platformio run -t test-marlin -e linux_native_test

unit-test-all-local-docker:
@if ! $(CONTAINER_RT_BIN) images -q $(CONTAINER_IMAGE) > /dev/null ; then $(MAKE) setup-local-docker ; fi
$(CONTAINER_RT_BIN) run $(CONTAINER_RT_OPTS) $(CONTAINER_IMAGE) make unit-test-all-local

setup-local-docker:
$(CONTAINER_RT_BIN) build -t $(CONTAINER_IMAGE) -f docker/Dockerfile .
$(CONTAINER_RT_BIN) buildx build -t $(CONTAINER_IMAGE) -f docker/Dockerfile .

PINS := $(shell find Marlin/src/pins -mindepth 2 -name '*.h')

Expand Down
5 changes: 4 additions & 1 deletion Marlin/src/HAL/LINUX/hardware/Timer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,10 @@ Timer::Timer() {
}

Timer::~Timer() {
timer_delete(timerid);
if (timerid != 0) {
timer_delete(timerid);
timerid = 0;
}
}

void Timer::init(uint32_t sig_id, uint32_t sim_freq, callback_fn* fn) {
Expand Down
2 changes: 2 additions & 0 deletions Marlin/src/HAL/LINUX/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
*/

#ifdef __PLAT_LINUX__
#ifndef UNIT_TEST

//#define GPIO_LOGGING // Full GPIO and Positional Logging

Expand Down Expand Up @@ -135,4 +136,5 @@ int main() {
read_serial.join();
}

#endif // UNIT_TEST
#endif // __PLAT_LINUX__
35 changes: 0 additions & 35 deletions Marlin/src/tests/marlin_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,41 +37,6 @@
// Startup tests are run at the end of setup()
void runStartupTests() {
// Call post-setup tests here to validate behaviors.

// String with cutoff at 20 chars:
// "F-string, 1234.50, 2"
SString<20> str20;
str20 = F("F-string, ");
str20.append(1234.5f).append(',').append(' ')
.append(2345.67).append(',').append(' ')
.echoln();

// Truncate to "F-string"
str20.trunc(8).echoln();

// 100 dashes, but chopped down to DEFAULT_MSTRING_SIZE (20)
TSS(repchr_t('-', 100)).echoln();

// Hello World!-123456------ <spaces!33
// ^ eol! ... 1234.50*2345.602 = 2895645.67
SString<100> str(F("Hello"));
str.append(F(" World!"));
str += '-';
str += uint8_t(123);
str += F("456");
str += repchr_t('-', 6);
str += Spaces(3);
str += "< spaces!";
str += int8_t(33);
str.eol();
str += "^ eol!";

str.append("...", 1234.5f, '*', p_float_t(2345.602, 3), F(" = "), 1234.5 * 2345.602).echoln();

// Print it again with SERIAL_ECHOLN
auto print_char_ptr = [](char * const str) { SERIAL_ECHOLN(str); };
print_char_ptr(str);

}

// Periodic tests are run from within loop()
Expand Down
5 changes: 5 additions & 0 deletions Marlin/tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
These test files are executed by the unit-tests built from the `<root>/test` folder.

These are placed outside of the main PlatformIO test folder so we can collect all test files and compile them into multiple PlatformIO test binaries. This enables tests to be executed against a variety of Marlin configurations.

To execute these tests, refer to the top-level Makefile.
58 changes: 58 additions & 0 deletions Marlin/tests/gcode/test_gcode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

#include "../test/unit_tests.h"
#include <src/gcode/gcode.h>
#include <src/gcode/parser.h>

MARLIN_TEST(gcode, process_parsed_command) {
GcodeSuite suite;
parser.command_letter = 'G';
parser.codenum = 0;
suite.process_parsed_command(false);
}

MARLIN_TEST(gcode, parse_g1_xz) {
char current_command[] = "G0 X10 Z30";
parser.command_letter = -128;
parser.codenum = -1;
parser.parse(current_command);
TEST_ASSERT_EQUAL('G', parser.command_letter);
TEST_ASSERT_EQUAL(0, parser.codenum);
TEST_ASSERT_TRUE(parser.seen('X'));
TEST_ASSERT_FALSE(parser.seen('Y'));
TEST_ASSERT_TRUE(parser.seen('Z'));
TEST_ASSERT_FALSE(parser.seen('E'));
}

MARLIN_TEST(gcode, parse_g1_nxz) {
char current_command[] = "N123 G0 X10 Z30";
parser.command_letter = -128;
parser.codenum = -1;
parser.parse(current_command);
TEST_ASSERT_EQUAL('G', parser.command_letter);
TEST_ASSERT_EQUAL(0, parser.codenum);
TEST_ASSERT_TRUE(parser.seen('X'));
TEST_ASSERT_FALSE(parser.seen('Y'));
TEST_ASSERT_TRUE(parser.seen('Z'));
TEST_ASSERT_FALSE(parser.seen('E'));
}
36 changes: 36 additions & 0 deletions Marlin/tests/runout/test_runout_sensor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Marlin 3D Printer Firmware
* Copyright (c) 2024 MarlinFirmware [https://github.com/MarlinFirmware/Marlin]
*
* Based on Sprinter and grbl.
* Copyright (c) 2011 Camiel Gubbels / Erik van der Zalm
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*
*/

#include "../test/unit_tests.h"

#if ENABLED(FILAMENT_RUNOUT_SENSOR)

#include <src/feature/runout.h>

MARLIN_TEST(runout, poll_runout_states) {
FilamentSensorBase sensor;
// Expected default value is one bit set for each extruder
uint8_t expected = static_cast<uint8_t>(~(~0u << NUM_RUNOUT_SENSORS));
TEST_ASSERT_EQUAL(expected, sensor.poll_runout_states());
}

#endif
Loading
Loading