|
@@ -0,0 +1,253 @@
|
|
|
|
|
+name: TaskScheduler Unit Test
|
|
|
|
|
+on:
|
|
|
|
|
+ push:
|
|
|
|
|
+ branches: [ main ]
|
|
|
|
|
+ pull_request:
|
|
|
|
|
+ branches: [ main ]
|
|
|
|
|
+ workflow_dispatch:
|
|
|
|
|
+
|
|
|
|
|
+jobs:
|
|
|
|
|
+ UnitTests_Linux:
|
|
|
|
|
+ runs-on: ubuntu-latest
|
|
|
|
|
+
|
|
|
|
|
+ steps:
|
|
|
|
|
+ - name: Checkout
|
|
|
|
|
+ uses: actions/checkout@main
|
|
|
|
|
+
|
|
|
|
|
+ - name: Install dependencies
|
|
|
|
|
+ run: |
|
|
|
|
|
+ sudo apt-get update
|
|
|
|
|
+ sudo apt-get install -y cmake build-essential libgtest-dev
|
|
|
|
|
+
|
|
|
|
|
+ - name: Build and install Google Test
|
|
|
|
|
+ run: |
|
|
|
|
|
+ cd /usr/src/gtest
|
|
|
|
|
+ sudo cmake .
|
|
|
|
|
+ sudo cmake --build . --target all
|
|
|
|
|
+ sudo cp lib/*.a /usr/lib
|
|
|
|
|
+
|
|
|
|
|
+ - name: Create test directory structure
|
|
|
|
|
+ run: |
|
|
|
|
|
+ mkdir -p test
|
|
|
|
|
+
|
|
|
|
|
+ - name: Create unit test file
|
|
|
|
|
+ run: |
|
|
|
|
|
+ cat > test/test_taskscheduler.cpp << 'EOF'
|
|
|
|
|
+ #include <gtest/gtest.h>
|
|
|
|
|
+ #include <iostream>
|
|
|
|
|
+ #include <vector>
|
|
|
|
|
+ #include <chrono>
|
|
|
|
|
+ #include <thread>
|
|
|
|
|
+
|
|
|
|
|
+ // Mock Arduino functions for Linux compilation
|
|
|
|
|
+ unsigned long millis() {
|
|
|
|
|
+ static auto start = std::chrono::steady_clock::now();
|
|
|
|
|
+ auto now = std::chrono::steady_clock::now();
|
|
|
|
|
+ return std::chrono::duration_cast<std::chrono::milliseconds>(now - start).count();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ unsigned long micros() {
|
|
|
|
|
+ static auto start = std::chrono::steady_clock::now();
|
|
|
|
|
+ auto now = std::chrono::steady_clock::now();
|
|
|
|
|
+ return std::chrono::duration_cast<std::chrono::microseconds>(now - start).count();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void delay(unsigned long ms) {
|
|
|
|
|
+ std::this_thread::sleep_for(std::chrono::milliseconds(ms));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Include TaskScheduler headers
|
|
|
|
|
+ #include "TaskScheduler.h"
|
|
|
|
|
+
|
|
|
|
|
+ // Global test output capture
|
|
|
|
|
+ std::vector<std::string> test_output;
|
|
|
|
|
+
|
|
|
|
|
+ // Test callback functions
|
|
|
|
|
+ void task1_callback() {
|
|
|
|
|
+ test_output.push_back("Task1 executed");
|
|
|
|
|
+ std::cout << "Task1 executed at " << millis() << "ms" << std::endl;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void task2_callback() {
|
|
|
|
|
+ test_output.push_back("Task2 executed");
|
|
|
|
|
+ std::cout << "Task2 executed at " << millis() << "ms" << std::endl;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void task3_callback() {
|
|
|
|
|
+ test_output.push_back("Task3 executed");
|
|
|
|
|
+ std::cout << "Task3 executed at " << millis() << "ms" << std::endl;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ class TaskSchedulerTest : public ::testing::Test {
|
|
|
|
|
+ protected:
|
|
|
|
|
+ void SetUp() override {
|
|
|
|
|
+ test_output.clear();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void TearDown() override {
|
|
|
|
|
+ test_output.clear();
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ TEST_F(TaskSchedulerTest, BasicSchedulerCreation) {
|
|
|
|
|
+ Scheduler ts;
|
|
|
|
|
+ EXPECT_TRUE(true); // Just test that scheduler can be created
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TEST_F(TaskSchedulerTest, SingleTaskExecution) {
|
|
|
|
|
+ Scheduler ts;
|
|
|
|
|
+
|
|
|
|
|
+ // Create a task that runs once after 100ms
|
|
|
|
|
+ Task task1(100, 1, &task1_callback, &ts, true);
|
|
|
|
|
+
|
|
|
|
|
+ unsigned long start_time = millis();
|
|
|
|
|
+ unsigned long timeout = start_time + 1000; // 1 second timeout
|
|
|
|
|
+
|
|
|
|
|
+ // Run scheduler until task executes or timeout
|
|
|
|
|
+ while (millis() < timeout && test_output.size() == 0) {
|
|
|
|
|
+ ts.execute();
|
|
|
|
|
+ delay(10); // Small delay to prevent busy waiting
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EXPECT_EQ(test_output.size(), 1);
|
|
|
|
|
+ EXPECT_EQ(test_output[0], "Task1 executed");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TEST_F(TaskSchedulerTest, MultipleTaskExecution) {
|
|
|
|
|
+ Scheduler ts;
|
|
|
|
|
+
|
|
|
|
|
+ // Create multiple tasks with different intervals
|
|
|
|
|
+ Task task1(100, 1, &task1_callback, &ts, true); // Run once after 100ms
|
|
|
|
|
+ Task task2(150, 1, &task2_callback, &ts, true); // Run once after 150ms
|
|
|
|
|
+ Task task3(200, 1, &task3_callback, &ts, true); // Run once after 200ms
|
|
|
|
|
+
|
|
|
|
|
+ unsigned long start_time = millis();
|
|
|
|
|
+ unsigned long timeout = start_time + 1000; // 1 second timeout
|
|
|
|
|
+
|
|
|
|
|
+ // Run scheduler until all tasks execute or timeout
|
|
|
|
|
+ while (millis() < timeout && test_output.size() < 3) {
|
|
|
|
|
+ ts.execute();
|
|
|
|
|
+ delay(10);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EXPECT_EQ(test_output.size(), 3);
|
|
|
|
|
+ EXPECT_EQ(test_output[0], "Task1 executed");
|
|
|
|
|
+ EXPECT_EQ(test_output[1], "Task2 executed");
|
|
|
|
|
+ EXPECT_EQ(test_output[2], "Task3 executed");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TEST_F(TaskSchedulerTest, RepeatingTask) {
|
|
|
|
|
+ Scheduler ts;
|
|
|
|
|
+
|
|
|
|
|
+ // Create a task that runs 3 times with 100ms interval
|
|
|
|
|
+ Task task1(100, 3, &task1_callback, &ts, true);
|
|
|
|
|
+
|
|
|
|
|
+ unsigned long start_time = millis();
|
|
|
|
|
+ unsigned long timeout = start_time + 1000; // 1 second timeout
|
|
|
|
|
+
|
|
|
|
|
+ // Run scheduler until task executes 3 times or timeout
|
|
|
|
|
+ while (millis() < timeout && test_output.size() < 3) {
|
|
|
|
|
+ ts.execute();
|
|
|
|
|
+ delay(10);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EXPECT_EQ(test_output.size(), 3);
|
|
|
|
|
+ for (int i = 0; i < 3; i++) {
|
|
|
|
|
+ EXPECT_EQ(test_output[i], "Task1 executed");
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ TEST_F(TaskSchedulerTest, TaskEnableDisable) {
|
|
|
|
|
+ Scheduler ts;
|
|
|
|
|
+
|
|
|
|
|
+ // Create a disabled task
|
|
|
|
|
+ Task task1(100, 1, &task1_callback, &ts, false);
|
|
|
|
|
+
|
|
|
|
|
+ unsigned long start_time = millis();
|
|
|
|
|
+ unsigned long timeout = start_time + 300;
|
|
|
|
|
+
|
|
|
|
|
+ // Run scheduler - task should not execute (it's disabled)
|
|
|
|
|
+ while (millis() < timeout) {
|
|
|
|
|
+ ts.execute();
|
|
|
|
|
+ delay(10);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EXPECT_EQ(test_output.size(), 0);
|
|
|
|
|
+
|
|
|
|
|
+ // Now enable the task
|
|
|
|
|
+ task1.enable();
|
|
|
|
|
+
|
|
|
|
|
+ start_time = millis();
|
|
|
|
|
+ timeout = start_time + 300;
|
|
|
|
|
+
|
|
|
|
|
+ // Run scheduler - task should execute now
|
|
|
|
|
+ while (millis() < timeout && test_output.size() == 0) {
|
|
|
|
|
+ ts.execute();
|
|
|
|
|
+ delay(10);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ EXPECT_EQ(test_output.size(), 1);
|
|
|
|
|
+ EXPECT_EQ(test_output[0], "Task1 executed");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ int main(int argc, char **argv) {
|
|
|
|
|
+ ::testing::InitGoogleTest(&argc, argv);
|
|
|
|
|
+ return RUN_ALL_TESTS();
|
|
|
|
|
+ }
|
|
|
|
|
+ EOF
|
|
|
|
|
+
|
|
|
|
|
+ - name: Create CMakeLists.txt
|
|
|
|
|
+ run: |
|
|
|
|
|
+ cat > CMakeLists.txt << 'EOF'
|
|
|
|
|
+ cmake_minimum_required(VERSION 3.10)
|
|
|
|
|
+ project(TaskSchedulerTests)
|
|
|
|
|
+
|
|
|
|
|
+ set(CMAKE_CXX_STANDARD 11)
|
|
|
|
|
+ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|
|
|
|
+
|
|
|
|
|
+ # Find packages
|
|
|
|
|
+ find_package(PkgConfig REQUIRED)
|
|
|
|
|
+ find_package(Threads REQUIRED)
|
|
|
|
|
+
|
|
|
|
|
+ # Include TaskScheduler source directory
|
|
|
|
|
+ include_directories(${CMAKE_SOURCE_DIR}/src)
|
|
|
|
|
+
|
|
|
|
|
+ # Add TaskScheduler source files
|
|
|
|
|
+ file(GLOB TASKSCHEDULER_SOURCES "src/*.cpp")
|
|
|
|
|
+
|
|
|
|
|
+ # Create the test executable
|
|
|
|
|
+ add_executable(
|
|
|
|
|
+ taskscheduler_tests
|
|
|
|
|
+ test/test_taskscheduler.cpp
|
|
|
|
|
+ ${TASKSCHEDULER_SOURCES}
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Link against gtest and pthread
|
|
|
|
|
+ target_link_libraries(
|
|
|
|
|
+ taskscheduler_tests
|
|
|
|
|
+ gtest
|
|
|
|
|
+ gtest_main
|
|
|
|
|
+ pthread
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Add compile definitions to make TaskScheduler work on Linux
|
|
|
|
|
+ target_compile_definitions(taskscheduler_tests PRIVATE
|
|
|
|
|
+ ARDUINO=200
|
|
|
|
|
+ _TASK_MICRO_RES=1
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ # Include directories
|
|
|
|
|
+ target_include_directories(taskscheduler_tests PRIVATE
|
|
|
|
|
+ src
|
|
|
|
|
+ test
|
|
|
|
|
+ )
|
|
|
|
|
+ EOF
|
|
|
|
|
+
|
|
|
|
|
+ - name: Build tests
|
|
|
|
|
+ run: |
|
|
|
|
|
+ cmake .
|
|
|
|
|
+ make
|
|
|
|
|
+
|
|
|
|
|
+ - name: Run unit tests
|
|
|
|
|
+ run: |
|
|
|
|
|
+ ./taskscheduler_tests
|