Skip to content

Commit

Permalink
Enable unit tests
Browse files Browse the repository at this point in the history
  • Loading branch information
vzahradnik committed Aug 3, 2024
1 parent 7b70aaf commit 62959a7
Show file tree
Hide file tree
Showing 16 changed files with 469 additions and 279 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: PlatformIO CI
name: Build
on: [push]

jobs:
Expand Down Expand Up @@ -34,6 +34,6 @@ jobs:
run: pip install --upgrade platformio

- name: Build PlatformIO examples
run: pio ci --lib "." --project-conf=platformio.ini
run: pio ci --project-conf=platformio.ini
env:
PLATFORMIO_CI_SRC: ${{ matrix.example }}
62 changes: 62 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
name: Test
on: [push]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- name: Install dependencies
run: sudo apt-get update && sudo apt-get install -y libsdl2-2.0-0
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install PlatformIO Core
run: pip install --upgrade platformio
- name: Set up QEMU
id: setup-qemu
run: |
if [[ "$(uname -m)" == "x86_64" ]]; then
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"
elif [[ "$(uname -m)" == "aarch64" ]]; then
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"
else
echo "Unsupported architecture: $(uname -m)"
exit 1
fi
wget $QEMU_URL -O qemu.tar.xz
mkdir -p qemu
tar -xf qemu.tar.xz -C qemu --strip-components=1
sudo mv qemu /usr/local/qemu
- name: Add QEMU to PATH
run: echo "/usr/local/qemu/bin" >> $GITHUB_PATH

- name: Run unit tests
run: pio test --without-uploading --project-conf=platformio-test.ini

static-analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/cache@v4
with:
path: |
~/.cache/pip
~/.platformio/.cache
key: ${{ runner.os }}-pio
- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Install PlatformIO Core
run: pip install --upgrade platformio

- name: Run static analysis
run: pio check
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
## TaskManagerIO scheduling and event based library for Arudino and mbed
[![PlatformIO](https://github.com/TcMenu/TaskManagerIO/actions/workflows/platformio.yml/badge.svg)](https://github.com/TcMenu/TaskManagerIO/actions/workflows/platformio.yml)
[![Build](https://github.com/TcMenu/TaskManagerIO/actions/workflows/build.yml/badge.svg)](https://github.com/TcMenu/TaskManagerIO/actions/workflows/build.yml)
[![Test](https://github.com/TcMenu/TaskManagerIO/actions/workflows/test.yml/badge.svg)](https://github.com/TcMenu/TaskManagerIO/actions/workflows/test.yml)
[![License: Apache 2.0](https://img.shields.io/badge/license-Apache--2.0-green.svg)](https://github.com/TcMenu/TaskManagerIO/blob/main/LICENSE)
[![GitHub release](https://img.shields.io/github/release/TcMenu/TaskManagerIO.svg?maxAge=3600)](https://github.com/TcMenu/TaskManagerIO/releases)
[![davetcc](https://img.shields.io/badge/davetcc-dev-blue.svg)](https://github.com/davetcc)
Expand Down
43 changes: 43 additions & 0 deletions merge-bin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
#!/usr/bin/python3

# Adds PlatformIO post-processing to merge all the ESP flash images into a single image.

import os

Import("env", "projenv")

board_config = env.BoardConfig()
firmware_bin = "${BUILD_DIR}/${PROGNAME}.bin"
merged_bin = os.environ.get("MERGED_BIN_PATH", "${BUILD_DIR}/${PROGNAME}-merged.bin")


def merge_bin_action(source, target, env):
flash_images = [
*env.Flatten(env.get("FLASH_EXTRA_IMAGES", [])),
"$ESP32_APP_OFFSET",
source[0].get_abspath(),
]
merge_cmd = " ".join(
[
'"$PYTHONEXE"',
'"$OBJCOPY"',
"--chip",
board_config.get("build.mcu", "esp32"),
"merge_bin",
"-o",
merged_bin,
"--flash_mode",
board_config.get("build.flash_mode", "dio"),
"--flash_freq",
"${__get_board_f_flash(__env__)}",
"--flash_size",
board_config.get("upload.flash_size", "4MB"),
"--fill-flash-size",
board_config.get("upload.flash_size", "4MB"),
*flash_images,
]
)
env.Execute(merge_cmd)


env.AddPostAction("buildprog", merge_bin_action)
17 changes: 17 additions & 0 deletions platformio-test.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[env:esp32dev]
platform = espressif32
framework = arduino
board = esp32dev
extra_scripts = post:merge-bin.py

lib_deps =
davetcc/IoAbstraction@^4.0.2
TaskManagerIO

test_testing_command =
qemu-system-xtensa
-nographic
-machine
esp32
-drive
file=${platformio.build_dir}/${this.__env__}/firmware-merged.bin,if=mtd,format=raw
1 change: 1 addition & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ framework = arduino

lib_deps =
davetcc/IoAbstraction@^4.0.2
TaskManagerIO

[env:megaatmega2560]
platform = atmelavr
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@

// Note: AVR test. Not supported in CI/CD.
#ifdef __AVR__

#include <testing/SimpleTest.h>
Expand Down
185 changes: 185 additions & 0 deletions test/test_core/taskManagerCoreTests.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#include <Arduino.h>
#include <unity.h>
#include <ExecWithParameter.h>
#include <IoLogging.h>
#include "TaskManagerIO.h"
#include "../utils/test_utils.h"

TimingHelpFixture fixture;

void setUp() {
fixture.setup();
}

void tearDown() {}

// these variables are set during test runs to time and verify tasks are run.
bool scheduled = false;
bool scheduled2ndJob = false;
unsigned long microsStarted = 0, microsExecuted = 0, microsExecuted2ndJob = 0;
int count1 = 0, count2 = 0;
uint8_t pinNo = 0;

void recordingJob() {
microsExecuted = micros();
count1++;
scheduled = true;
}

void recordingJob2() {
microsExecuted2ndJob = micros();
count2++;
scheduled2ndJob = true;
}


class TestingExec : public Executable {
public:
int noOfTimesRun;

TestingExec() {
noOfTimesRun = 0;
}

void exec() override {
noOfTimesRun++;
}
};

TestingExec exec;

void testRunningUsingExecutorClass() {
taskManager.scheduleFixedRate(10, &::exec);
taskManager.scheduleOnce(250, recordingJob);

fixture.assertThatTaskRunsOnTime(250000L, MILLIS_ALLOWANCE);
TEST_ASSERT_GREATER_THAN(10, ::exec.noOfTimesRun);
}

void testSchedulingTaskOnceInMicroseconds() {
taskManager.scheduleOnce(800, recordingJob, TIME_MICROS);
fixture.assertThatTaskRunsOnTime(800, MICROS_ALLOWANCE);
fixture.assertTasksSpacesTaken(0);
}

void testSchedulingTaskOnceInMilliseconds() {
taskManager.scheduleOnce(20, recordingJob, TIME_MILLIS);
fixture.assertThatTaskRunsOnTime(19500, MILLIS_ALLOWANCE);
fixture.assertTasksSpacesTaken(0);
}

void testSchedulingTaskOnceInSeconds() {
taskManager.scheduleOnce(2, recordingJob, TIME_SECONDS);
// Second scheduling is not as granular, we need to allow +- 100mS.
fixture.assertThatTaskRunsOnTime(2000000L, MILLIS_ALLOWANCE);
fixture.assertTasksSpacesTaken(0);
}

void testScheduleManyJobsAtOnce() {
taskManager.scheduleOnce(1, [] {}, TIME_SECONDS);
taskManager.scheduleOnce(200, recordingJob, TIME_MILLIS);
taskManager.scheduleOnce(250, recordingJob2, TIME_MICROS);

fixture.assertThatTaskRunsOnTime(199500, MILLIS_ALLOWANCE);
fixture.assertThatSecondJobRan(250, MICROS_ALLOWANCE);
fixture.assertTasksSpacesTaken(1);
}

void testEnableAndDisableSupport() {
static int myTaskCounter = 0;
auto myTaskId = taskManager.scheduleFixedRate(1, [] { myTaskCounter++; }, TIME_MILLIS);
taskManager.yieldForMicros(20000);
TEST_ASSERT_NOT_EQUAL(0, myTaskCounter);

// "turn off" the task
taskManager.setTaskEnabled(myTaskId, false);

// It can take one cycle for the task to switch enablement state.
taskManager.yieldForMicros(2000);
auto oldTaskCount = myTaskCounter;

// Now run the task for some time, it should never get scheduled.
taskManager.yieldForMicros(20000);
TEST_ASSERT_EQUAL(myTaskCounter, oldTaskCount);

// "turn on" the task and see if it increases again
taskManager.setTaskEnabled(myTaskId, true);
taskManager.yieldForMicros(20000);
TEST_ASSERT_NOT_EQUAL(myTaskCounter, oldTaskCount);
}

void testScheduleFixedRate() {
TEST_ASSERT_EQUAL(nullptr, taskManager.getFirstTask());

auto taskId1 = taskManager.scheduleFixedRate(10, recordingJob, TIME_MILLIS);
auto taskId2 = taskManager.scheduleFixedRate(100, recordingJob2, TIME_MICROS);

// Now check the task registration in detail.
TEST_ASSERT_NOT_EQUAL(TASKMGR_INVALIDID, taskId1);
TimerTask* task = taskManager.getFirstTask();
TEST_ASSERT_NOT_EQUAL(nullptr, task);
TEST_ASSERT_FALSE(task->isMillisSchedule());
TEST_ASSERT_TRUE(task->isMicrosSchedule());

// Now check the task registration in detail.
TEST_ASSERT_NOT_EQUAL(TASKMGR_INVALIDID, taskId2);
task = task->getNext();
TEST_ASSERT_NOT_EQUAL(nullptr, task);
TEST_ASSERT_TRUE(task->isMillisSchedule());
TEST_ASSERT_FALSE(task->isMicrosSchedule());

dumpTasks();

uint32_t timeStartYield = millis();
taskManager.yieldForMicros(secondsToMillis(22));
uint32_t timeTaken = millis() - timeStartYield;

dumpTasks();

// Make sure the yield timings were in range.
TEST_ASSERT_LESS_THAN(25U, timeTaken);
TEST_ASSERT_GREATER_THAN(19U, timeTaken);

// Now make sure that we got in the right ballpark of calls.
TEST_ASSERT_GREATER_THAN(1, count1);
TEST_ASSERT_GREATER_THAN(150, count2);
}

void testCancellingAJobAfterCreation() {
TEST_ASSERT_EQUAL(nullptr, taskManager.getFirstTask());

auto taskId = taskManager.scheduleFixedRate(10, recordingJob, TIME_MILLIS);

// Now check the task registration in detail.
TEST_ASSERT_NOT_EQUAL(TASKMGR_INVALIDID, taskId);
TimerTask* task = taskManager.getFirstTask();
TEST_ASSERT_NOT_EQUAL(nullptr, task);
TEST_ASSERT_TRUE(task->isMillisSchedule());
TEST_ASSERT_FALSE(task->isMicrosSchedule());
TEST_ASSERT_GREATER_THAN(8000UL, task->microsFromNow());

fixture.assertThatTaskRunsOnTime(10000, MILLIS_ALLOWANCE);

// Cancel the task and make sure everything is cleared down
fixture.assertTasksSpacesTaken(1);
taskManager.cancelTask(taskId);
taskManager.yieldForMicros(100); // Needs to run the cancellation task.
fixture.assertTasksSpacesTaken(0);

TEST_ASSERT_EQUAL(nullptr, taskManager.getFirstTask());
}

void setup() {
UNITY_BEGIN();
RUN_TEST(testRunningUsingExecutorClass);
RUN_TEST(testSchedulingTaskOnceInMicroseconds);
RUN_TEST(testSchedulingTaskOnceInMilliseconds);
RUN_TEST(testSchedulingTaskOnceInSeconds);
RUN_TEST(testScheduleManyJobsAtOnce);
RUN_TEST(testEnableAndDisableSupport);
RUN_TEST(testScheduleFixedRate);
RUN_TEST(testCancellingAJobAfterCreation);
UNITY_END();
}

void loop() {}
Loading

0 comments on commit 62959a7

Please sign in to comment.