Skip to content

Commit 62959a7

Browse files
committed
Enable unit tests
1 parent 7b70aaf commit 62959a7

16 files changed

Lines changed: 469 additions & 279 deletions

File tree

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: PlatformIO CI
1+
name: Build
22
on: [push]
33

44
jobs:
@@ -34,6 +34,6 @@ jobs:
3434
run: pip install --upgrade platformio
3535

3636
- name: Build PlatformIO examples
37-
run: pio ci --lib "." --project-conf=platformio.ini
37+
run: pio ci --project-conf=platformio.ini
3838
env:
3939
PLATFORMIO_CI_SRC: ${{ matrix.example }}

.github/workflows/test.yml

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
name: Test
2+
on: [push]
3+
4+
jobs:
5+
test:
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: actions/checkout@v4
9+
- uses: actions/cache@v4
10+
with:
11+
path: |
12+
~/.cache/pip
13+
~/.platformio/.cache
14+
key: ${{ runner.os }}-pio
15+
- name: Install dependencies
16+
run: sudo apt-get update && sudo apt-get install -y libsdl2-2.0-0
17+
- uses: actions/setup-python@v5
18+
with:
19+
python-version: '3.12'
20+
- name: Install PlatformIO Core
21+
run: pip install --upgrade platformio
22+
- name: Set up QEMU
23+
id: setup-qemu
24+
run: |
25+
if [[ "$(uname -m)" == "x86_64" ]]; then
26+
QEMU_URL="https://github.com/espressif/qemu/releases/download/esp-develop-8.2.0-20240122/qemu-xtensa-softmmu-esp_develop_8.2.0_20240122-x86_64-linux-gnu.tar.xz"
27+
elif [[ "$(uname -m)" == "aarch64" ]]; then
28+
QEMU_URL="https://github.com/espressif/qemu/releases/download/esp-develop-8.2.0-20240122/qemu-xtensa-softmmu-esp_develop_8.2.0_20240122-aarch64-linux-gnu.tar.xz"
29+
else
30+
echo "Unsupported architecture: $(uname -m)"
31+
exit 1
32+
fi
33+
wget $QEMU_URL -O qemu.tar.xz
34+
mkdir -p qemu
35+
tar -xf qemu.tar.xz -C qemu --strip-components=1
36+
sudo mv qemu /usr/local/qemu
37+
38+
- name: Add QEMU to PATH
39+
run: echo "/usr/local/qemu/bin" >> $GITHUB_PATH
40+
41+
- name: Run unit tests
42+
run: pio test --without-uploading --project-conf=platformio-test.ini
43+
44+
static-analysis:
45+
runs-on: ubuntu-latest
46+
steps:
47+
- uses: actions/checkout@v4
48+
- uses: actions/cache@v4
49+
with:
50+
path: |
51+
~/.cache/pip
52+
~/.platformio/.cache
53+
key: ${{ runner.os }}-pio
54+
- uses: actions/setup-python@v5
55+
with:
56+
python-version: '3.12'
57+
58+
- name: Install PlatformIO Core
59+
run: pip install --upgrade platformio
60+
61+
- name: Run static analysis
62+
run: pio check

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
## TaskManagerIO scheduling and event based library for Arudino and mbed
2-
[![PlatformIO](https://github.com/TcMenu/TaskManagerIO/actions/workflows/platformio.yml/badge.svg)](https://github.com/TcMenu/TaskManagerIO/actions/workflows/platformio.yml)
2+
[![Build](https://github.com/TcMenu/TaskManagerIO/actions/workflows/build.yml/badge.svg)](https://github.com/TcMenu/TaskManagerIO/actions/workflows/build.yml)
3+
[![Test](https://github.com/TcMenu/TaskManagerIO/actions/workflows/test.yml/badge.svg)](https://github.com/TcMenu/TaskManagerIO/actions/workflows/test.yml)
34
[![License: Apache 2.0](https://img.shields.io/badge/license-Apache--2.0-green.svg)](https://github.com/TcMenu/TaskManagerIO/blob/main/LICENSE)
45
[![GitHub release](https://img.shields.io/github/release/TcMenu/TaskManagerIO.svg?maxAge=3600)](https://github.com/TcMenu/TaskManagerIO/releases)
56
[![davetcc](https://img.shields.io/badge/davetcc-dev-blue.svg)](https://github.com/davetcc)

merge-bin.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
#!/usr/bin/python3
2+
3+
# Adds PlatformIO post-processing to merge all the ESP flash images into a single image.
4+
5+
import os
6+
7+
Import("env", "projenv")
8+
9+
board_config = env.BoardConfig()
10+
firmware_bin = "${BUILD_DIR}/${PROGNAME}.bin"
11+
merged_bin = os.environ.get("MERGED_BIN_PATH", "${BUILD_DIR}/${PROGNAME}-merged.bin")
12+
13+
14+
def merge_bin_action(source, target, env):
15+
flash_images = [
16+
*env.Flatten(env.get("FLASH_EXTRA_IMAGES", [])),
17+
"$ESP32_APP_OFFSET",
18+
source[0].get_abspath(),
19+
]
20+
merge_cmd = " ".join(
21+
[
22+
'"$PYTHONEXE"',
23+
'"$OBJCOPY"',
24+
"--chip",
25+
board_config.get("build.mcu", "esp32"),
26+
"merge_bin",
27+
"-o",
28+
merged_bin,
29+
"--flash_mode",
30+
board_config.get("build.flash_mode", "dio"),
31+
"--flash_freq",
32+
"${__get_board_f_flash(__env__)}",
33+
"--flash_size",
34+
board_config.get("upload.flash_size", "4MB"),
35+
"--fill-flash-size",
36+
board_config.get("upload.flash_size", "4MB"),
37+
*flash_images,
38+
]
39+
)
40+
env.Execute(merge_cmd)
41+
42+
43+
env.AddPostAction("buildprog", merge_bin_action)

platformio-test.ini

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
[env:esp32dev]
2+
platform = espressif32
3+
framework = arduino
4+
board = esp32dev
5+
extra_scripts = post:merge-bin.py
6+
7+
lib_deps =
8+
davetcc/IoAbstraction@^4.0.2
9+
TaskManagerIO
10+
11+
test_testing_command =
12+
qemu-system-xtensa
13+
-nographic
14+
-machine
15+
esp32
16+
-drive
17+
file=${platformio.build_dir}/${this.__env__}/firmware-merged.bin,if=mtd,format=raw

platformio.ini

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ framework = arduino
33

44
lib_deps =
55
davetcc/IoAbstraction@^4.0.2
6+
TaskManagerIO
67

78
[env:megaatmega2560]
89
platform = atmelavr

tests/taskMgrTests/avrClockRollTests.cpp renamed to test/avrClockRollTests/avrClockRollTests.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
1+
// Note: AVR test. Not supported in CI/CD.
22
#ifdef __AVR__
33

44
#include <testing/SimpleTest.h>
Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
#include <Arduino.h>
2+
#include <unity.h>
3+
#include <ExecWithParameter.h>
4+
#include <IoLogging.h>
5+
#include "TaskManagerIO.h"
6+
#include "../utils/test_utils.h"
7+
8+
TimingHelpFixture fixture;
9+
10+
void setUp() {
11+
fixture.setup();
12+
}
13+
14+
void tearDown() {}
15+
16+
// these variables are set during test runs to time and verify tasks are run.
17+
bool scheduled = false;
18+
bool scheduled2ndJob = false;
19+
unsigned long microsStarted = 0, microsExecuted = 0, microsExecuted2ndJob = 0;
20+
int count1 = 0, count2 = 0;
21+
uint8_t pinNo = 0;
22+
23+
void recordingJob() {
24+
microsExecuted = micros();
25+
count1++;
26+
scheduled = true;
27+
}
28+
29+
void recordingJob2() {
30+
microsExecuted2ndJob = micros();
31+
count2++;
32+
scheduled2ndJob = true;
33+
}
34+
35+
36+
class TestingExec : public Executable {
37+
public:
38+
int noOfTimesRun;
39+
40+
TestingExec() {
41+
noOfTimesRun = 0;
42+
}
43+
44+
void exec() override {
45+
noOfTimesRun++;
46+
}
47+
};
48+
49+
TestingExec exec;
50+
51+
void testRunningUsingExecutorClass() {
52+
taskManager.scheduleFixedRate(10, &::exec);
53+
taskManager.scheduleOnce(250, recordingJob);
54+
55+
fixture.assertThatTaskRunsOnTime(250000L, MILLIS_ALLOWANCE);
56+
TEST_ASSERT_GREATER_THAN(10, ::exec.noOfTimesRun);
57+
}
58+
59+
void testSchedulingTaskOnceInMicroseconds() {
60+
taskManager.scheduleOnce(800, recordingJob, TIME_MICROS);
61+
fixture.assertThatTaskRunsOnTime(800, MICROS_ALLOWANCE);
62+
fixture.assertTasksSpacesTaken(0);
63+
}
64+
65+
void testSchedulingTaskOnceInMilliseconds() {
66+
taskManager.scheduleOnce(20, recordingJob, TIME_MILLIS);
67+
fixture.assertThatTaskRunsOnTime(19500, MILLIS_ALLOWANCE);
68+
fixture.assertTasksSpacesTaken(0);
69+
}
70+
71+
void testSchedulingTaskOnceInSeconds() {
72+
taskManager.scheduleOnce(2, recordingJob, TIME_SECONDS);
73+
// Second scheduling is not as granular, we need to allow +- 100mS.
74+
fixture.assertThatTaskRunsOnTime(2000000L, MILLIS_ALLOWANCE);
75+
fixture.assertTasksSpacesTaken(0);
76+
}
77+
78+
void testScheduleManyJobsAtOnce() {
79+
taskManager.scheduleOnce(1, [] {}, TIME_SECONDS);
80+
taskManager.scheduleOnce(200, recordingJob, TIME_MILLIS);
81+
taskManager.scheduleOnce(250, recordingJob2, TIME_MICROS);
82+
83+
fixture.assertThatTaskRunsOnTime(199500, MILLIS_ALLOWANCE);
84+
fixture.assertThatSecondJobRan(250, MICROS_ALLOWANCE);
85+
fixture.assertTasksSpacesTaken(1);
86+
}
87+
88+
void testEnableAndDisableSupport() {
89+
static int myTaskCounter = 0;
90+
auto myTaskId = taskManager.scheduleFixedRate(1, [] { myTaskCounter++; }, TIME_MILLIS);
91+
taskManager.yieldForMicros(20000);
92+
TEST_ASSERT_NOT_EQUAL(0, myTaskCounter);
93+
94+
// "turn off" the task
95+
taskManager.setTaskEnabled(myTaskId, false);
96+
97+
// It can take one cycle for the task to switch enablement state.
98+
taskManager.yieldForMicros(2000);
99+
auto oldTaskCount = myTaskCounter;
100+
101+
// Now run the task for some time, it should never get scheduled.
102+
taskManager.yieldForMicros(20000);
103+
TEST_ASSERT_EQUAL(myTaskCounter, oldTaskCount);
104+
105+
// "turn on" the task and see if it increases again
106+
taskManager.setTaskEnabled(myTaskId, true);
107+
taskManager.yieldForMicros(20000);
108+
TEST_ASSERT_NOT_EQUAL(myTaskCounter, oldTaskCount);
109+
}
110+
111+
void testScheduleFixedRate() {
112+
TEST_ASSERT_EQUAL(nullptr, taskManager.getFirstTask());
113+
114+
auto taskId1 = taskManager.scheduleFixedRate(10, recordingJob, TIME_MILLIS);
115+
auto taskId2 = taskManager.scheduleFixedRate(100, recordingJob2, TIME_MICROS);
116+
117+
// Now check the task registration in detail.
118+
TEST_ASSERT_NOT_EQUAL(TASKMGR_INVALIDID, taskId1);
119+
TimerTask* task = taskManager.getFirstTask();
120+
TEST_ASSERT_NOT_EQUAL(nullptr, task);
121+
TEST_ASSERT_FALSE(task->isMillisSchedule());
122+
TEST_ASSERT_TRUE(task->isMicrosSchedule());
123+
124+
// Now check the task registration in detail.
125+
TEST_ASSERT_NOT_EQUAL(TASKMGR_INVALIDID, taskId2);
126+
task = task->getNext();
127+
TEST_ASSERT_NOT_EQUAL(nullptr, task);
128+
TEST_ASSERT_TRUE(task->isMillisSchedule());
129+
TEST_ASSERT_FALSE(task->isMicrosSchedule());
130+
131+
dumpTasks();
132+
133+
uint32_t timeStartYield = millis();
134+
taskManager.yieldForMicros(secondsToMillis(22));
135+
uint32_t timeTaken = millis() - timeStartYield;
136+
137+
dumpTasks();
138+
139+
// Make sure the yield timings were in range.
140+
TEST_ASSERT_LESS_THAN(25U, timeTaken);
141+
TEST_ASSERT_GREATER_THAN(19U, timeTaken);
142+
143+
// Now make sure that we got in the right ballpark of calls.
144+
TEST_ASSERT_GREATER_THAN(1, count1);
145+
TEST_ASSERT_GREATER_THAN(150, count2);
146+
}
147+
148+
void testCancellingAJobAfterCreation() {
149+
TEST_ASSERT_EQUAL(nullptr, taskManager.getFirstTask());
150+
151+
auto taskId = taskManager.scheduleFixedRate(10, recordingJob, TIME_MILLIS);
152+
153+
// Now check the task registration in detail.
154+
TEST_ASSERT_NOT_EQUAL(TASKMGR_INVALIDID, taskId);
155+
TimerTask* task = taskManager.getFirstTask();
156+
TEST_ASSERT_NOT_EQUAL(nullptr, task);
157+
TEST_ASSERT_TRUE(task->isMillisSchedule());
158+
TEST_ASSERT_FALSE(task->isMicrosSchedule());
159+
TEST_ASSERT_GREATER_THAN(8000UL, task->microsFromNow());
160+
161+
fixture.assertThatTaskRunsOnTime(10000, MILLIS_ALLOWANCE);
162+
163+
// Cancel the task and make sure everything is cleared down
164+
fixture.assertTasksSpacesTaken(1);
165+
taskManager.cancelTask(taskId);
166+
taskManager.yieldForMicros(100); // Needs to run the cancellation task.
167+
fixture.assertTasksSpacesTaken(0);
168+
169+
TEST_ASSERT_EQUAL(nullptr, taskManager.getFirstTask());
170+
}
171+
172+
void setup() {
173+
UNITY_BEGIN();
174+
RUN_TEST(testRunningUsingExecutorClass);
175+
RUN_TEST(testSchedulingTaskOnceInMicroseconds);
176+
RUN_TEST(testSchedulingTaskOnceInMilliseconds);
177+
RUN_TEST(testSchedulingTaskOnceInSeconds);
178+
RUN_TEST(testScheduleManyJobsAtOnce);
179+
RUN_TEST(testEnableAndDisableSupport);
180+
RUN_TEST(testScheduleFixedRate);
181+
RUN_TEST(testCancellingAJobAfterCreation);
182+
UNITY_END();
183+
}
184+
185+
void loop() {}

0 commit comments

Comments
 (0)