diff --git a/.github/workflows/platformio.yml b/.github/workflows/build.yml similarity index 93% rename from .github/workflows/platformio.yml rename to .github/workflows/build.yml index bf8c6c2..fac6e72 100644 --- a/.github/workflows/platformio.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: PlatformIO CI +name: Build on: [push] jobs: @@ -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 }} \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..afce0cd --- /dev/null +++ b/.github/workflows/test.yml @@ -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 diff --git a/README.md b/README.md index 0096453..12ac23a 100644 --- a/README.md +++ b/README.md @@ -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) diff --git a/merge-bin.py b/merge-bin.py new file mode 100644 index 0000000..a063ab9 --- /dev/null +++ b/merge-bin.py @@ -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) diff --git a/platformio-test.ini b/platformio-test.ini new file mode 100644 index 0000000..f970c25 --- /dev/null +++ b/platformio-test.ini @@ -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 diff --git a/platformio.ini b/platformio.ini index 19deeaf..f0974b8 100644 --- a/platformio.ini +++ b/platformio.ini @@ -3,6 +3,7 @@ framework = arduino lib_deps = davetcc/IoAbstraction@^4.0.2 + TaskManagerIO [env:megaatmega2560] platform = atmelavr diff --git a/tests/taskMgrTests/avrClockRollTests.cpp b/test/avrClockRollTests/avrClockRollTests.cpp similarity index 98% rename from tests/taskMgrTests/avrClockRollTests.cpp rename to test/avrClockRollTests/avrClockRollTests.cpp index 48f3369..c27a780 100644 --- a/tests/taskMgrTests/avrClockRollTests.cpp +++ b/test/avrClockRollTests/avrClockRollTests.cpp @@ -1,4 +1,4 @@ - +// Note: AVR test. Not supported in CI/CD. #ifdef __AVR__ #include diff --git a/test/test_core/taskManagerCoreTests.cpp b/test/test_core/taskManagerCoreTests.cpp new file mode 100644 index 0000000..73a8de2 --- /dev/null +++ b/test/test_core/taskManagerCoreTests.cpp @@ -0,0 +1,185 @@ +#include +#include +#include +#include +#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() {} \ No newline at end of file diff --git a/tests/taskMgrTests/eventTestCases.cpp b/test/test_event/eventTestCases.cpp similarity index 66% rename from tests/taskMgrTests/eventTestCases.cpp rename to test/test_event/eventTestCases.cpp index 1ddabf2..62d6623 100644 --- a/tests/taskMgrTests/eventTestCases.cpp +++ b/test/test_event/eventTestCases.cpp @@ -1,10 +1,23 @@ - -#include +#include +#include #include #include "TaskManagerIO.h" -#include "test_utils.h" +#include "../utils/test_utils.h" + +TimingHelpFixture fixture; + +void setUp() { + fixture.setup(); +} -using namespace SimpleTest; +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; bool taskWithinEvent; @@ -59,25 +72,25 @@ bool runScheduleUntilMatchOrTimeout(TMPredicate predicate) { return predicate(); } -testF(TimingHelpFixture, testRaiseEventStartTaskCompleted) { +void testRaiseEventStartTaskCompleted() { EnsureExecutionWithin timelyChecker(600); // first register the event taskManager.registerEvent(&polledEvent); // then we - assertTrue(runScheduleUntilMatchOrTimeout([] { return polledEvent.getScheduleCalls() >= 3; } )); + TEST_ASSERT_TRUE(runScheduleUntilMatchOrTimeout([] { return polledEvent.getScheduleCalls() >= 3; })); // and now we tell the event to trigger itself polledEvent.startTriggering(); // wait until the exec() method is called - assertTrue(runScheduleUntilMatchOrTimeout([] { return polledEvent.getExecCalls() != 0; })); + TEST_ASSERT_TRUE(runScheduleUntilMatchOrTimeout([] { return polledEvent.getExecCalls() != 0; })); // and then make sure that the task registed inside the event triggers - assertTrue(runScheduleUntilMatchOrTimeout([] { return taskWithinEvent; })); + TEST_ASSERT_TRUE(runScheduleUntilMatchOrTimeout([] { return taskWithinEvent; })); - assertTrue(timelyChecker.ensureTimely()); + TEST_ASSERT_TRUE(timelyChecker.ensureTimely()); } class TestExternalEvent : public BaseEvent { @@ -115,16 +128,16 @@ class TestExternalEvent : public BaseEvent { int getExecCalls() const { return execCalls; } } externalEvent; -testF(TimingHelpFixture, testNotifyEventThatStartsAnotherTask) { +void testNotifyEventThatStartsAnotherTask() { EnsureExecutionWithin timelyChecker(100); auto taskId = taskManager.registerEvent(&externalEvent); - for(int i=0; i<100; i++) { + for(int i = 0; i < 100; i++) { taskWithinEvent = false; externalEvent.markTriggeredAndNotify(); taskManager.yieldForMicros(100); - assertTrue(runScheduleUntilMatchOrTimeout([] { return taskWithinEvent; })); - assertEquals(i + 1, externalEvent.getExecCalls()); + TEST_ASSERT_TRUE(runScheduleUntilMatchOrTimeout([] { return taskWithinEvent; })); + TEST_ASSERT_EQUAL(i + 1, externalEvent.getExecCalls()); } // now we let the task complete and after one more cycle it should be removed by task manager. @@ -132,15 +145,24 @@ testF(TimingHelpFixture, testNotifyEventThatStartsAnotherTask) { externalEvent.markTriggeredAndNotify(); taskManager.yieldForMicros(100); - //now it should be completely removed, and whatever we do should not affect task manager + // now it should be completely removed, and whatever we do should not affect task manager externalEvent.resetStats(); externalEvent.markTriggeredAndNotify(); taskManager.yieldForMicros(200); - assertFalse(externalEvent.wasNextCheckCalled()); - assertEquals(0, externalEvent.getExecCalls()); + TEST_ASSERT_FALSE(externalEvent.wasNextCheckCalled()); + TEST_ASSERT_EQUAL(0, externalEvent.getExecCalls()); // it should not be in task manager any longer. - assertFalse(taskManager.getTask(taskId)->isEvent()); + TEST_ASSERT_FALSE(taskManager.getTask(taskId)->isEvent()); + + TEST_ASSERT_TRUE(timelyChecker.ensureTimely()); +} - assertTrue(timelyChecker.ensureTimely()); +void setup() { + UNITY_BEGIN(); + RUN_TEST(testRaiseEventStartTaskCompleted); + RUN_TEST(testNotifyEventThatStartsAnotherTask); + UNITY_END(); } + +void loop() {} \ No newline at end of file diff --git a/tests/taskMgrTests/HighThroughputTestCases.cpp b/test/test_high_throughput/HighThroughputTestCases.cpp similarity index 74% rename from tests/taskMgrTests/HighThroughputTestCases.cpp rename to test/test_high_throughput/HighThroughputTestCases.cpp index e6e27da..34a9ef7 100644 --- a/tests/taskMgrTests/HighThroughputTestCases.cpp +++ b/test/test_high_throughput/HighThroughputTestCases.cpp @@ -1,9 +1,11 @@ - -#include +#include +#include #include -#include "test_utils.h" +#include "../utils/test_utils.h" + +void setUp() {} -using namespace SimpleTest; +void tearDown() {} int counts[6]; @@ -41,7 +43,7 @@ void testCall5() { // schedules, this is the most important test to pass in the whole suite. // -class HighThroughputFixture : public UnitTestExecutor { +class HighThroughputFixture { public: /** * This method checks that taskmanager tasks are in proper and stable order. And that only running tasks @@ -71,7 +73,7 @@ class HighThroughputFixture : public UnitTestExecutor { if(!inOrder) dumpTasks(); // assert that it's in order. - assertTrue(inOrder); + TEST_ASSERT_TRUE(inOrder); } void clearCounts() { @@ -80,10 +82,11 @@ class HighThroughputFixture : public UnitTestExecutor { } }; -testF(HighThroughputFixture, taskManagerHighThroughputTest) { - char slotData[32]; - clearCounts(); +void taskManagerHighThroughputTest() { + HighThroughputFixture fixture; + fixture.clearCounts(); + char slotData[32]; serdebugF2("Dumping threads", taskManager.checkAvailableSlots(slotData, sizeof slotData)); taskManager.scheduleFixedRate(10, testCall1); @@ -95,19 +98,19 @@ testF(HighThroughputFixture, taskManagerHighThroughputTest) { serdebugF2("Dumping threads", taskManager.checkAvailableSlots(slotData, sizeof slotData)); unsigned long start = millis(); - while(counts[5] < 10 && (millis() - start) < 25000) { + while (counts[5] < 10 && (millis() - start) < 25000) { taskManager.yieldForMicros(10000); - assertTasksAreInOrder(); + fixture.assertTasksAreInOrder(); } serdebugF2("Dumping threads", taskManager.checkAvailableSlots(slotData, sizeof slotData)); - assertEquals(10, counts[5]); // should be 10 runs as it's manually repeating - assertMoreThan(140, counts[1]); // should be at least 140 runs, scheduled every 100 millis, - assertMoreThan(14, counts[3]); // should be at least 14 runs, as this test lasts about 20 seconds. - assertMoreThan(1400, counts[0]); // should be at least 1400 runs it's scheduled every 10 millis - assertEquals(1, counts[4]); // should have been triggered once - assertNotEquals(0, counts[2]); // meaningless to count micros calls. check it happened + TEST_ASSERT_EQUAL(10, counts[5]); // should be 10 runs as it's manually repeating + TEST_ASSERT_GREATER_THAN(140, counts[1]); // should be at least 140 runs, scheduled every 100 millis + TEST_ASSERT_GREATER_THAN(14, counts[3]); // should be at least 14 runs, as this test lasts about 20 seconds + TEST_ASSERT_GREATER_THAN(1400, counts[0]); // should be at least 1400 runs it's scheduled every 10 millis + TEST_ASSERT_EQUAL(1, counts[4]); // should have been triggered once + TEST_ASSERT_NOT_EQUAL(0, counts[2]); // meaningless to count micros calls. check it happened } // @@ -119,7 +122,11 @@ uint8_t taskId1; bool taskCancelled; int storedCount1; int storedCount2; -testF(HighThroughputFixture, testCancellingsTasksWithinAnotherTask) { + +void testCancellingsTasksWithinAnotherTask() { + HighThroughputFixture fixture; + fixture.clearCounts(); + char slotData[15]; // set up for the run by clearing down state. @@ -141,7 +148,7 @@ testF(HighThroughputFixture, testCancellingsTasksWithinAnotherTask) { storedCount2 = counts[2]; }); - assertTasksAreInOrder(); + fixture.assertTasksAreInOrder(); // now run the task manager until the job gets cancelled (or it times out) int count = 1000; @@ -151,8 +158,8 @@ testF(HighThroughputFixture, testCancellingsTasksWithinAnotherTask) { // the cancelled job should have run at least once before cancellation // and then must have been cancelled. Tasks should be in order - assertNotEquals(0, counts[0]); - assertTrue(taskCancelled); + TEST_ASSERT_NOT_EQUAL(0, counts[0]); + TEST_ASSERT_TRUE(taskCancelled); count = 500; while (--count != 0) { @@ -161,7 +168,7 @@ testF(HighThroughputFixture, testCancellingsTasksWithinAnotherTask) { // once the task manager has been scheduled again, the call counts should not be the same and the tasks // should remain in order - assertTasksAreInOrder(); + fixture.assertTasksAreInOrder(); // in this case we dump the queue, something is wrong. if (counts[1] == storedCount1) { @@ -169,7 +176,15 @@ testF(HighThroughputFixture, testCancellingsTasksWithinAnotherTask) { serdebugF2("Dumping threads", taskManager.checkAvailableSlots(slotData, sizeof slotData)); } - assertNotEquals(counts[1], storedCount1); - assertNotEquals(counts[2], storedCount1); + TEST_ASSERT_NOT_EQUAL(counts[1], storedCount1); + TEST_ASSERT_NOT_EQUAL(counts[2], storedCount1); +} + +void setup() { + UNITY_BEGIN(); + RUN_TEST(taskManagerHighThroughputTest); + RUN_TEST(testCancellingsTasksWithinAnotherTask); + UNITY_END(); } +void loop() {} \ No newline at end of file diff --git a/tests/taskMgrTests/InterruptTestCases.cpp b/test/test_interrupt/InterruptTestCases.cpp similarity index 58% rename from tests/taskMgrTests/InterruptTestCases.cpp rename to test/test_interrupt/InterruptTestCases.cpp index b655c0c..e86f96e 100644 --- a/tests/taskMgrTests/InterruptTestCases.cpp +++ b/test/test_interrupt/InterruptTestCases.cpp @@ -1,9 +1,22 @@ - -#include +#include +#include #include "TaskManagerIO.h" -#include "test_utils.h" +#include "../utils/test_utils.h" + +TimingHelpFixture fixture; + +void setUp() { + fixture.setup(); +} -using namespace SimpleTest; +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; class MockedInterruptAbstraction : public InterruptAbstraction { private: @@ -49,21 +62,29 @@ void intHandler(pinid_t pin) { MockedInterruptAbstraction interruptAbs; -testF(TimingHelpFixture, interruptSupportMarshalling) { +void testInterruptSupportMarshalling() { taskManager.setInterruptCallback(intHandler); taskManager.addInterrupt(&interruptAbs, 2, CHANGE); // make sure the interrupt is properly registered. - assertEquals(pintype_t(2), interruptAbs.getInterruptPin()); - assertEquals(CHANGE, interruptAbs.getTheMode()); - assertFalse(interruptAbs.isIntHandlerNull()); + TEST_ASSERT_EQUAL_UINT8(2, interruptAbs.getInterruptPin()); + TEST_ASSERT_EQUAL(CHANGE, interruptAbs.getTheMode()); + TEST_ASSERT_FALSE(interruptAbs.isIntHandlerNull()); // now pretend the interrupt took place. - (interruptAbs.runInterrupt()); + interruptAbs.runInterrupt(); // and wait for task manager to schedule. - assertThatTaskRunsOnTime(0, 250); + fixture.assertThatTaskRunsOnTime(0, 250); // and the pin should be 2 - assertEquals(2, pinNo); + TEST_ASSERT_EQUAL(2, pinNo); +} + +void setup() { + UNITY_BEGIN(); + RUN_TEST(testInterruptSupportMarshalling); + UNITY_END(); } + +void loop() {} diff --git a/tests/taskMgrTests/reentrantLockingTests.cpp b/test/test_reentrant_locking/reentrantLockingTests.cpp similarity index 68% rename from tests/taskMgrTests/reentrantLockingTests.cpp rename to test/test_reentrant_locking/reentrantLockingTests.cpp index 2bc2efb..be2c123 100644 --- a/tests/taskMgrTests/reentrantLockingTests.cpp +++ b/test/test_reentrant_locking/reentrantLockingTests.cpp @@ -1,10 +1,12 @@ - -#include +#include +#include #include #include "TaskManagerIO.h" -#include "test_utils.h" +#include "../utils/test_utils.h" + +void setUp() {} -using namespace SimpleTest; +void tearDown() {} SimpleSpinLock testLock; taskid_t runTaskId1; @@ -18,21 +20,20 @@ bool allGood = false; int runCount1, runCount2, runCount3; int captureCount2, captureCount3; -test(testGettingRunningTaskAlwaysCorrect) { +void testGettingRunningTaskAlwaysCorrect() { task1RunningPtrCheck = false; task2RunningPtrCheck = false; runCount1 = runCount2 = runCount3 = 0; taskManager.reset(); - assertEquals(nullptr, taskManager.getRunningTask()); + TEST_ASSERT_EQUAL(nullptr, taskManager.getRunningTask()); serdebugF("Starting running task check"); runTaskId1 = taskManager.scheduleFixedRate(1, [] { task1RunningPtrCheck = taskManager.getRunningTask() == taskManager.getTask(runTaskId1); - if(task1RunningPtrCheck) { + if (task1RunningPtrCheck) { taskManager.yieldForMicros(millisToMicros(1)); - task1RunningPtrCheck = taskManager.getRunningTask() == taskManager.getTask(runTaskId1); } runCount1++; @@ -48,12 +49,20 @@ test(testGettingRunningTaskAlwaysCorrect) { unsigned long then = millis(); taskManager.yieldForMicros(millisToMicros(100)); int diff = int(millis() - then); - assertMoreThan(90, diff); + TEST_ASSERT_GREATER_THAN(90, diff); serdebugF("Finished running task check, asserting."); - assertTrue(task1RunningPtrCheck); - assertTrue(task2RunningPtrCheck); - assertMoreThan(30, runCount1); - assertMoreThan(250, runCount2); + TEST_ASSERT_TRUE(task1RunningPtrCheck); + TEST_ASSERT_TRUE(task2RunningPtrCheck); + TEST_ASSERT_GREATER_THAN(30, runCount1); + TEST_ASSERT_GREATER_THAN(250, runCount2); } + +void setup() { + UNITY_BEGIN(); + RUN_TEST(testGettingRunningTaskAlwaysCorrect); + UNITY_END(); +} + +void loop() {} diff --git a/tests/timerInterruptTest/timerInterruptTest.cpp b/test/timerInterruptTest/timerInterruptTest.cpp similarity index 96% rename from tests/timerInterruptTest/timerInterruptTest.cpp rename to test/timerInterruptTest/timerInterruptTest.cpp index 58eecbc..9bac2f7 100644 --- a/tests/timerInterruptTest/timerInterruptTest.cpp +++ b/test/timerInterruptTest/timerInterruptTest.cpp @@ -1,4 +1,4 @@ - +// Note: STM32 test. Not supported in CI/CD. #include using namespace SimpleTest; diff --git a/tests/taskMgrTests/test_utils.h b/test/utils/test_utils.h similarity index 75% rename from tests/taskMgrTests/test_utils.h rename to test/utils/test_utils.h index 6eb57fb..2fd3373 100644 --- a/tests/taskMgrTests/test_utils.h +++ b/test/utils/test_utils.h @@ -1,8 +1,8 @@ - #ifndef TASKMANGERIO_TEST_UTILS_H #define TASKMANGERIO_TEST_UTILS_H -#include +#include +#include #include void dumpTasks(); @@ -19,8 +19,6 @@ extern int count1; extern int count2; extern uint8_t pinNo; -using namespace SimpleTest; - class EnsureExecutionWithin { private: unsigned long startMillis; @@ -41,9 +39,9 @@ class EnsureExecutionWithin { } }; -class TimingHelpFixture : public SimpleTest::UnitTestExecutor { -protected: - void setup() override { +class TimingHelpFixture { +public: + void setup() { taskManager.reset(); scheduled = scheduled2ndJob = false; microsExecuted = microsExecuted2ndJob = 0; @@ -60,7 +58,7 @@ class TimingHelpFixture : public SimpleTest::UnitTestExecutor { } // we must have called the job. - assertTrue(scheduled); + TEST_ASSERT_TRUE(scheduled); // print information to help with diagnostics. serdebugF4("Scheduled: ", microsStarted, " exec:", microsExecuted); @@ -68,10 +66,7 @@ class TimingHelpFixture : public SimpleTest::UnitTestExecutor { serdebugF4(" difference ", howLong, " expected:", minExpected); // check that it has been executed within the allowable window. - unsigned long minRange = (minExpected < allowanceOver) ? 0 : (minExpected - allowanceOver); - unsigned long maxRange = minExpected + allowanceOver; - assertMoreThan(minRange, (microsExecuted - microsStarted)); - assertLessThan(maxRange, (microsExecuted - microsStarted)); + TEST_ASSERT_UINT32_WITHIN(allowanceOver, minExpected, howLong); } void assertThatSecondJobRan(unsigned long minExpected, unsigned long allowanceOver) { @@ -83,11 +78,8 @@ class TimingHelpFixture : public SimpleTest::UnitTestExecutor { long howLong = microsExecuted2ndJob - microsStarted; serdebugF4(" difference ", howLong, " - expected ", minExpected); - assertTrue(scheduled2ndJob); - unsigned long minRange = (minExpected < allowanceOver) ? 0 : (minExpected - allowanceOver); - unsigned long maxRange = minExpected + allowanceOver; - assertMoreThan(minRange, (microsExecuted2ndJob - microsStarted)); - assertLessThan(maxRange, (microsExecuted2ndJob - microsStarted)); + TEST_ASSERT_TRUE(scheduled2ndJob); + TEST_ASSERT_UINT32_WITHIN(allowanceOver, minExpected, howLong); } void assertTasksSpacesTaken(int taken) { @@ -99,9 +91,21 @@ class TimingHelpFixture : public SimpleTest::UnitTestExecutor { ++taskData; } serdebugF2("Tasks free ", cnt); - assertEquals(taken, cnt); + TEST_ASSERT_EQUAL(taken, cnt); } }; +void dumpTasks() { + serdebugF("Dumping the task queue contents"); + TimerTask* task = taskManager.getFirstTask(); + while(task) { + serdebugF4(" - Task schedule ", task->microsFromNow(), task->isRepeating() ? " Repeating ":" Once ", (task->isMicrosSchedule() ? " Micros " : " Millis ")); + serdebug(task->isInUse() ? " InUse":" Free"); + if(task->getNext() == task) { + serdebugF("!!!Infinite loop found!!!"); + } + task = task->getNext(); + } +} #endif //TASKMANGERIO_TEST_UTILS_H diff --git a/tests/taskMgrTests/taskManagerCoreTests.cpp b/tests/taskMgrTests/taskManagerCoreTests.cpp deleted file mode 100644 index f1bbe4d..0000000 --- a/tests/taskMgrTests/taskManagerCoreTests.cpp +++ /dev/null @@ -1,176 +0,0 @@ - -#include -#include -#include "TaskManagerIO.h" -#include "test_utils.h" - -using namespace SimpleTest; - -void dumpTasks() { - serdebugF("Dumping the task queue contents"); - TimerTask* task = taskManager.getFirstTask(); - while(task) { - serdebugF4(" - Task schedule ", task->microsFromNow(), task->isRepeating() ? " Repeating ":" Once ", (task->isMicrosSchedule() ? " Micros " : " Millis ")); - serdebug(task->isInUse() ? " InUse":" Free"); - if(task->getNext() == task) { - serdebugF("!!!Infinite loop found!!!"); - } - task = task->getNext(); - } -} - -// 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; - -testF(TimingHelpFixture, testRunningUsingExecutorClass) { - taskManager.scheduleFixedRate(10, &::exec); - taskManager.scheduleOnce(250, recordingJob); - assertThatTaskRunsOnTime(250000L, MILLIS_ALLOWANCE); - assertMoreThan(10, ::exec.noOfTimesRun); -} - -testF(TimingHelpFixture, schedulingTaskOnceInMicroseconds) { - taskManager.scheduleOnce(800, recordingJob, TIME_MICROS); - assertThatTaskRunsOnTime(800, MICROS_ALLOWANCE); - assertTasksSpacesTaken(0); -} - -testF(TimingHelpFixture, schedulingTaskOnceInMilliseconds) { - taskManager.scheduleOnce(20, recordingJob, TIME_MILLIS); - assertThatTaskRunsOnTime(19500, MILLIS_ALLOWANCE); - assertTasksSpacesTaken(0); -} - -testF(TimingHelpFixture, schedulingTaskOnceInSeconds) { - taskManager.scheduleOnce(2, recordingJob, TIME_SECONDS); - // second scheduling is not as granular, we need to allow +- 100mS. - assertThatTaskRunsOnTime(2000000L, MILLIS_ALLOWANCE); - assertTasksSpacesTaken(0); -} - -testF(TimingHelpFixture, scheduleManyJobsAtOnce) { - taskManager.scheduleOnce(1, [] {}, TIME_SECONDS); - taskManager.scheduleOnce(200, recordingJob, TIME_MILLIS); - taskManager.scheduleOnce(250, recordingJob2, TIME_MICROS); - - assertThatTaskRunsOnTime(199500, MILLIS_ALLOWANCE); - assertThatSecondJobRan(250, MICROS_ALLOWANCE); - assertTasksSpacesTaken(1); -} - -testF(TimingHelpFixture, enableAndDisableSupport) { - static int myTaskCounter = 0; - auto myTaskId = taskManager.scheduleFixedRate(1, [] { myTaskCounter++; }, TIME_MILLIS); - taskManager.yieldForMicros(20000); - assertNotEquals(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); - assertEquals(myTaskCounter, oldTaskCount); - - // "turn on" the task and see if it increases again - taskManager.setTaskEnabled(myTaskId, true); - taskManager.yieldForMicros(20000); - assertNotEquals(myTaskCounter, oldTaskCount); - -} - -testF(TimingHelpFixture, scheduleFixedRateTestCase) { - assertEquals(taskManager.getFirstTask(), NULL); - - auto taskId1 = taskManager.scheduleFixedRate(10, recordingJob, TIME_MILLIS); - auto taskId2 = taskManager.scheduleFixedRate(100, recordingJob2, TIME_MICROS); - - // now check the task registration in detail. - assertNotEquals(taskId1, TASKMGR_INVALIDID); - TimerTask* task = taskManager.getFirstTask(); - assertNotEquals(task, NULL); - assertFalse(task->isMillisSchedule()); - assertTrue(task->isMicrosSchedule()); - - // now check the task registration in detail. - assertNotEquals(taskId2, TASKMGR_INVALIDID); - task = task->getNext(); - assertNotEquals(task, NULL); - assertTrue(task->isMillisSchedule()); - assertFalse(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. - assertLessThan((uint32_t) 25, timeTaken); - assertMoreThan((uint32_t) 19, timeTaken); - - // now make sure that we got in the right ball park of calls. - assertMoreThan(1, count1); - assertMoreThan(150, count2); -} - -testF(TimingHelpFixture, cancellingAJobAfterCreation) { - assertEquals(taskManager.getFirstTask(), nullptr); - - auto taskId = taskManager.scheduleFixedRate(10, recordingJob, TIME_MILLIS); - - // now check the task registration in detail. - assertNotEquals(taskId, TASKMGR_INVALIDID); - TimerTask* task = taskManager.getFirstTask(); - assertNotEquals(task, nullptr); - assertTrue(task->isMillisSchedule()); - assertFalse(task->isMicrosSchedule()); - assertMoreThan(8000UL, task->microsFromNow()); - - assertThatTaskRunsOnTime(10000, MILLIS_ALLOWANCE); - - // cancel the task and make sure everything is cleared down - assertTasksSpacesTaken(1); - taskManager.cancelTask(taskId); - taskManager.yieldForMicros(100); // needs to run the cancellation task. - assertTasksSpacesTaken(0); - - assertEquals(taskManager.getFirstTask(), NULL); -} diff --git a/tests/taskMgrTests/taskMgrTests.cpp b/tests/taskMgrTests/taskMgrTests.cpp deleted file mode 100644 index 1ef8602..0000000 --- a/tests/taskMgrTests/taskMgrTests.cpp +++ /dev/null @@ -1,14 +0,0 @@ - -#include -#include - -using namespace SimpleTest; - -IOLOG_MBED_PORT_IF_NEEDED(USBTX, USBRX) - -void setup() { - IOLOG_START_SERIAL - startTesting(); -} - -DEFAULT_TEST_RUNLOOP