|
|
@@ -0,0 +1,1914 @@
|
|
|
+// test-scheduler-basic-thorough.cpp - Comprehensive unit tests for TaskScheduler library
|
|
|
+// This file contains thorough tests covering ALL basic methods of Task and Scheduler classes
|
|
|
+// without any compile options enabled (basic functionality only)
|
|
|
+//
|
|
|
+// ═══════════════════════════════════════════════════════════════════════════════════════
|
|
|
+// COMPREHENSIVE TEST PLAN AND COVERAGE MATRIX
|
|
|
+// ═══════════════════════════════════════════════════════════════════════════════════════
|
|
|
+//
|
|
|
+// TASK CLASS METHODS TESTED:
|
|
|
+// ═══════════════════════════════════════════════════════════════════════════════════════
|
|
|
+//
|
|
|
+// ┌─ CONSTRUCTORS ─────────────────────────────────────────────────────────────────────┐
|
|
|
+// │ • Task() - Default constructor (no parameters) │
|
|
|
+// │ • Task(interval, iterations, - Parameterized constructor with all options │
|
|
|
+// │ callback, scheduler, - Tests auto-addition to scheduler │
|
|
|
+// │ enable, onEnable, - Tests initial enabled state │
|
|
|
+// │ onDisable) - Tests callback assignment │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ INFORMATION METHODS ──────────────────────────────────────────────────────────────┐
|
|
|
+// │ • isEnabled() - Returns current enabled/disabled state │
|
|
|
+// │ • getInterval() - Returns execution interval in milliseconds │
|
|
|
+// │ • getIterations() - Returns remaining iterations count │
|
|
|
+// │ • getRunCounter() - Returns number of times task has executed │
|
|
|
+// │ - Increments before callback execution │
|
|
|
+// │ - Resets to 0 when task is enabled │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ CONTROL METHODS ──────────────────────────────────────────────────────────────────┐
|
|
|
+// │ • enable() - Enables task for execution │
|
|
|
+// │ - Calls OnEnable callback if defined │
|
|
|
+// │ - Schedules immediate execution │
|
|
|
+// │ - Returns true if enabled successfully │
|
|
|
+// │ • enableIfNot() - Enables only if currently disabled │
|
|
|
+// │ - Returns previous enabled state │
|
|
|
+// │ - Prevents double-enabling │
|
|
|
+// │ • disable() - Disables task execution │
|
|
|
+// │ - Calls OnDisable callback if defined │
|
|
|
+// │ - Returns previous enabled state │
|
|
|
+// │ • restart() - Resets iterations to original count │
|
|
|
+// │ - Re-enables task │
|
|
|
+// │ - Schedules immediate execution │
|
|
|
+// │ • restartDelayed(delay) - Same as restart() but with delay │
|
|
|
+// │ - Useful for periodic restarts │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ TIMING METHODS ───────────────────────────────────────────────────────────────────┐
|
|
|
+// │ • delay() - Delays next execution by current interval │
|
|
|
+// │ • delay(milliseconds) - Delays next execution by specified time │
|
|
|
+// │ - Does not change enabled state │
|
|
|
+// │ • forceNextIteration() - Forces immediate execution on next pass │
|
|
|
+// │ - Resets internal timing counters │
|
|
|
+// │ - Useful for urgent task execution │
|
|
|
+// │ • enableDelayed() - Enables with current interval delay │
|
|
|
+// │ • enableDelayed(delay) - Enables with specified delay │
|
|
|
+// │ - Combines enable() + delay() operations │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ CONFIGURATION METHODS ────────────────────────────────────────────────────────────┐
|
|
|
+// │ • set(interval, iterations, - Sets all task parameters at once │
|
|
|
+// │ callback, onEnable, - Convenient for reconfiguring existing tasks │
|
|
|
+// │ onDisable) - Does not enable task automatically │
|
|
|
+// │ • setInterval(milliseconds) - Changes execution interval │
|
|
|
+// │ - Applies delay automatically │
|
|
|
+// │ - Next execution uses new interval │
|
|
|
+// │ • setIterations(count) - Sets remaining iteration count │
|
|
|
+// │ - Use TASK_FOREVER (-1) for infinite │
|
|
|
+// │ - Does not enable task │
|
|
|
+// │ • setCallback(function) - Changes main execution callback │
|
|
|
+// │ - Takes effect on next execution │
|
|
|
+// │ • setOnEnable(function) - Sets callback for enable events │
|
|
|
+// │ - Must return bool (true = enable success) │
|
|
|
+// │ • setOnDisable(function) - Sets callback for disable events │
|
|
|
+// │ - Called only when disabling enabled task │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ ITERATION STATE METHODS ──────────────────────────────────────────────────────────┐
|
|
|
+// │ • isFirstIteration() - Returns true during first execution │
|
|
|
+// │ - Based on runCounter value │
|
|
|
+// │ - Useful for initialization logic │
|
|
|
+// │ • isLastIteration() - Returns true during final execution │
|
|
|
+// │ - Based on remaining iterations │
|
|
|
+// │ - Useful for cleanup logic │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ CALLBACK SWITCHING METHODS ───────────────────────────────────────────────────────┐
|
|
|
+// │ • yield(callback) - Switches to new callback immediately │
|
|
|
+// │ - Preserves iteration count and interval │
|
|
|
+// │ - Enables cooperative multitasking │
|
|
|
+// │ - Forces immediate execution with new callback │
|
|
|
+// │ • yieldOnce(callback) - Switches to callback for single execution │
|
|
|
+// │ - Sets iterations to 1 │
|
|
|
+// │ - Task disables after callback execution │
|
|
|
+// │ - Useful for one-time operations │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// SCHEDULER CLASS METHODS TESTED:
|
|
|
+// ═══════════════════════════════════════════════════════════════════════════════════════
|
|
|
+//
|
|
|
+// ┌─ CONSTRUCTOR AND INITIALIZATION ───────────────────────────────────────────────────┐
|
|
|
+// │ • Scheduler() - Default constructor │
|
|
|
+// │ - Creates empty task chain │
|
|
|
+// │ - Initializes internal state │
|
|
|
+// │ • init() - Reinitializes scheduler state │
|
|
|
+// │ - Clears internal timing variables │
|
|
|
+// │ - Preserves task chain │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ TASK MANAGEMENT METHODS ──────────────────────────────────────────────────────────┐
|
|
|
+// │ • addTask(task) - Adds task to execution chain │
|
|
|
+// │ - Appends to end of chain │
|
|
|
+// │ - Sets task's scheduler reference │
|
|
|
+// │ - Prevents duplicate additions │
|
|
|
+// │ • deleteTask(task) - Removes task from execution chain │
|
|
|
+// │ - Relinks remaining tasks │
|
|
|
+// │ - Clears task's scheduler reference │
|
|
|
+// │ - Safe to call on non-existent tasks │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ EXECUTION CONTROL METHODS ────────────────────────────────────────────────────────┐
|
|
|
+// │ • execute() - Processes one scheduling pass │
|
|
|
+// │ - Evaluates all tasks in chain order │
|
|
|
+// │ - Returns true if pass was idle │
|
|
|
+// │ - Returns false if any task executed │
|
|
|
+// │ - Should be called repeatedly in main loop │
|
|
|
+// │ • enableAll() - Enables all tasks in chain │
|
|
|
+// │ - Calls each task's enable() method │
|
|
|
+// │ - Useful for batch operations │
|
|
|
+// │ • disableAll() - Disables all tasks in chain │
|
|
|
+// │ - Calls each task's disable() method │
|
|
|
+// │ - Useful for emergency stops │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ TIME QUERY METHODS ───────────────────────────────────────────────────────────────┐
|
|
|
+// │ • timeUntilNextIteration(task) - Returns milliseconds until task executes │
|
|
|
+// │ - Returns -1 for disabled tasks │
|
|
|
+// │ - Returns 0 for immediately scheduled tasks │
|
|
|
+// │ - Accounts for current timing state │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ TASK ACCESS METHODS ──────────────────────────────────────────────────────────────┐
|
|
|
+// │ • currentTask() - Returns reference to currently executing task │
|
|
|
+// │ - Valid during callback execution │
|
|
|
+// │ - Also valid during OnEnable/OnDisable │
|
|
|
+// │ • getCurrentTask() - Returns pointer to currently executing task │
|
|
|
+// │ - More flexible than currentTask() │
|
|
|
+// │ - Returns nullptr when no task executing │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// ┌─ TIMING CONTROL METHODS ───────────────────────────────────────────────────────────┐
|
|
|
+// │ • startNow() - Resets all task timing to execute immediately│
|
|
|
+// │ - Affects all enabled tasks in chain │
|
|
|
+// │ - Useful after long initialization periods │
|
|
|
+// │ - Does not enable disabled tasks │
|
|
|
+// └────────────────────────────────────────────────────────────────────────────────────┘
|
|
|
+//
|
|
|
+// TEST DESIGN PRINCIPLES:
|
|
|
+// ═══════════════════════════════════════════════════════════════════════════════════════
|
|
|
+// • Each method tested individually with comprehensive verification
|
|
|
+// • Edge cases and error conditions thoroughly covered
|
|
|
+// • Task lifecycle validated from creation to destruction
|
|
|
+// • Multiple task interaction scenarios tested
|
|
|
+// • Timing accuracy verified with appropriate tolerances
|
|
|
+// • Callback invocation order and parameters validated
|
|
|
+// • State transitions and side effects verified
|
|
|
+// • Integration tests ensure methods work together correctly
|
|
|
+//
|
|
|
+// SPECIAL TEST CATEGORIES:
|
|
|
+// ═══════════════════════════════════════════════════════════════════════════════════════
|
|
|
+// • Integration Tests: Complex real-world scenarios
|
|
|
+// • Edge Case Tests: Zero iterations, infinite iterations, null callbacks
|
|
|
+// • Error Handling: Invalid states, boundary conditions
|
|
|
+// • Performance Tests: Multiple concurrent tasks
|
|
|
+// • Lifecycle Tests: Complete task creation through destruction flows
|
|
|
+//
|
|
|
+
|
|
|
+#include <gtest/gtest.h>
|
|
|
+#include "Arduino.h"
|
|
|
+#include "TaskScheduler.h"
|
|
|
+
|
|
|
+// Global test state for capturing callback executions
|
|
|
+std::vector<std::string> test_output;
|
|
|
+int callback_counter = 0;
|
|
|
+bool onEnable_called = false;
|
|
|
+bool onDisable_called = false;
|
|
|
+
|
|
|
+// Test callback functions
|
|
|
+void basic_callback() {
|
|
|
+ callback_counter++;
|
|
|
+ test_output.push_back("basic_callback_" + std::to_string(callback_counter));
|
|
|
+}
|
|
|
+
|
|
|
+void callback_1() {
|
|
|
+ test_output.push_back("callback_1");
|
|
|
+}
|
|
|
+
|
|
|
+void callback_2() {
|
|
|
+ test_output.push_back("callback_2");
|
|
|
+}
|
|
|
+
|
|
|
+void callback_3() {
|
|
|
+ test_output.push_back("callback_3");
|
|
|
+}
|
|
|
+
|
|
|
+void multi_step_callback_1() {
|
|
|
+ test_output.push_back("step_1");
|
|
|
+}
|
|
|
+
|
|
|
+void multi_step_callback_2() {
|
|
|
+ test_output.push_back("step_2");
|
|
|
+}
|
|
|
+
|
|
|
+void multi_step_callback_3() {
|
|
|
+ test_output.push_back("step_3");
|
|
|
+}
|
|
|
+
|
|
|
+bool test_onEnable() {
|
|
|
+ onEnable_called = true;
|
|
|
+ test_output.push_back("onEnable_called");
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+bool test_onEnable_false() {
|
|
|
+ onEnable_called = true;
|
|
|
+ test_output.push_back("onEnable_called_false");
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+void test_onDisable() {
|
|
|
+ onDisable_called = true;
|
|
|
+ test_output.push_back("onDisable_called");
|
|
|
+}
|
|
|
+
|
|
|
+// Test fixture for comprehensive scheduler testing
|
|
|
+class SchedulerThoroughTest : public ::testing::Test {
|
|
|
+protected:
|
|
|
+ void SetUp() override {
|
|
|
+ clearTestOutput();
|
|
|
+ callback_counter = 0;
|
|
|
+ onEnable_called = false;
|
|
|
+ onDisable_called = false;
|
|
|
+ millis(); // Initialize timing
|
|
|
+ }
|
|
|
+
|
|
|
+ void TearDown() override {
|
|
|
+ clearTestOutput();
|
|
|
+ callback_counter = 0;
|
|
|
+ onEnable_called = false;
|
|
|
+ onDisable_called = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ bool runSchedulerUntil(Scheduler& ts, std::function<bool()> condition, unsigned long timeout_ms = 1000) {
|
|
|
+ return waitForCondition([&]() {
|
|
|
+ ts.execute();
|
|
|
+ return condition();
|
|
|
+ }, timeout_ms);
|
|
|
+ }
|
|
|
+};
|
|
|
+
|
|
|
+// ================== TASK CONSTRUCTOR TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task default constructor behavior
|
|
|
+ *
|
|
|
+ * TESTS: Task()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that a Task created with the default constructor initializes
|
|
|
+ * all properties to safe default values and is in a predictable state.
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Task should be disabled by default (safety)
|
|
|
+ * - Interval should be 0 (no automatic execution)
|
|
|
+ * - Iterations should be 0 (won't execute)
|
|
|
+ * - RunCounter should be 0 (hasn't executed yet)
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Default constructor must create a safe, inert task that won't
|
|
|
+ * execute unexpectedly. This is critical for safe initialization patterns.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskDefaultConstructor) {
|
|
|
+ Task task;
|
|
|
+
|
|
|
+ // Verify task is in safe, inert state after default construction
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Should not execute without explicit enable
|
|
|
+ EXPECT_EQ(task.getInterval(), 0); // No automatic timing
|
|
|
+ EXPECT_EQ(task.getIterations(), 0); // Won't execute without iterations set
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 0); // No executions yet
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task parameterized constructor with all options
|
|
|
+ *
|
|
|
+ * TESTS: Task(interval, iterations, callback, scheduler, enable)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that the full constructor properly sets all task parameters
|
|
|
+ * and correctly associates the task with a scheduler.
|
|
|
+ *
|
|
|
+ * PARAMETERS TESTED:
|
|
|
+ * - interval: 1000ms (1 second execution interval)
|
|
|
+ * - iterations: 5 (task will execute 5 times then auto-disable)
|
|
|
+ * - callback: basic_callback function pointer
|
|
|
+ * - scheduler: reference to scheduler for automatic addition
|
|
|
+ * - enable: false (task starts disabled for manual control)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - All parameters should be set exactly as specified
|
|
|
+ * - Task should be disabled initially (enable=false)
|
|
|
+ * - RunCounter starts at 0 (no executions yet)
|
|
|
+ * - Task should be automatically added to scheduler's chain
|
|
|
+ *
|
|
|
+ * IMPORTANCE: This tests the most common Task creation pattern and ensures
|
|
|
+ * all parameters are correctly stored and accessible.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskParameterizedConstructor) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(1000, 5, &basic_callback, &ts, false);
|
|
|
+
|
|
|
+ // Verify all constructor parameters were set correctly
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // enable=false parameter
|
|
|
+ EXPECT_EQ(task.getInterval(), 1000); // 1000ms interval parameter
|
|
|
+ EXPECT_EQ(task.getIterations(), 5); // 5 iterations parameter
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 0); // No executions yet
|
|
|
+ // Note: callback and scheduler assignment tested in execution tests
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task constructor with OnEnable/OnDisable callbacks
|
|
|
+ *
|
|
|
+ * TESTS: Task(interval, iterations, callback, scheduler, enable, onEnable, onDisable)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that the full constructor with lifecycle callbacks correctly
|
|
|
+ * stores callback pointers without invoking them during construction.
|
|
|
+ *
|
|
|
+ * LIFECYCLE CALLBACKS:
|
|
|
+ * - onEnable: Called when task is enabled (should return bool)
|
|
|
+ * - onDisable: Called when task is disabled
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - All basic parameters set correctly
|
|
|
+ * - Callback pointers stored but not invoked during construction
|
|
|
+ * - Global callback flags remain false (not called yet)
|
|
|
+ * - Task ready for enable/disable lifecycle events
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Validates that lifecycle callbacks are properly registered
|
|
|
+ * without premature invocation, ensuring controlled task lifecycle management.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskConstructorWithOnEnableOnDisable) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(500, 3, &basic_callback, &ts, false, &test_onEnable, &test_onDisable);
|
|
|
+
|
|
|
+ // Verify basic parameters are set correctly
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+ EXPECT_EQ(task.getInterval(), 500);
|
|
|
+ EXPECT_EQ(task.getIterations(), 3);
|
|
|
+
|
|
|
+ // Verify lifecycle callbacks are registered but not yet called
|
|
|
+ EXPECT_FALSE(onEnable_called); // onEnable not called during construction
|
|
|
+ EXPECT_FALSE(onDisable_called); // onDisable not called during construction
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task constructor with automatic enable
|
|
|
+ *
|
|
|
+ * TESTS: Task(..., enable=true) + immediate execution behavior
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that tasks created with enable=true are immediately ready
|
|
|
+ * for execution and will run on the first scheduler pass.
|
|
|
+ *
|
|
|
+ * AUTO-ENABLE BEHAVIOR:
|
|
|
+ * - Task becomes enabled immediately during construction
|
|
|
+ * - Task is scheduled for immediate execution (no delay)
|
|
|
+ * - First scheduler pass should execute the task
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Task should be enabled after construction
|
|
|
+ * - Task should execute successfully on first scheduler run
|
|
|
+ * - Callback counter should increment correctly
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Tests the convenience constructor that creates immediately
|
|
|
+ * active tasks, commonly used for initialization or startup tasks.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskConstructorAutoEnabled) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 1, &basic_callback, &ts, true); // enable=true
|
|
|
+
|
|
|
+ // Verify task is enabled immediately after construction
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+
|
|
|
+ // Verify task executes on first scheduler pass
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success); // Execution should succeed
|
|
|
+ EXPECT_EQ(callback_counter, 1); // Should execute exactly once
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK INFORMATION METHODS TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test all Task information getter methods through task lifecycle
|
|
|
+ *
|
|
|
+ * TESTS: isEnabled(), getInterval(), getIterations(), getRunCounter()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that information methods return correct values at different
|
|
|
+ * stages of task lifecycle and properly track state changes.
|
|
|
+ *
|
|
|
+ * METHODS TESTED:
|
|
|
+ * - isEnabled(): Returns current enabled/disabled state
|
|
|
+ * - getInterval(): Returns execution interval in milliseconds
|
|
|
+ * - getIterations(): Returns remaining iterations (decrements after each run)
|
|
|
+ * - getRunCounter(): Returns total executions since last enable (increments)
|
|
|
+ *
|
|
|
+ * LIFECYCLE STAGES TESTED:
|
|
|
+ * 1. Initial state (after construction)
|
|
|
+ * 2. After enabling (before execution)
|
|
|
+ * 3. After first execution (state changes)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Initial: disabled, interval=2000, iterations=10, runCounter=0
|
|
|
+ * - After enable: enabled, same interval/iterations, runCounter still 0
|
|
|
+ * - After execution: enabled, same interval, iterations=9, runCounter=1
|
|
|
+ *
|
|
|
+ * IMPORTANCE: These methods are fundamental for task monitoring and debugging.
|
|
|
+ * Accurate state reporting is critical for application logic and diagnostics.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskInformationMethods) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(2000, 10, &basic_callback, &ts, false);
|
|
|
+
|
|
|
+ // Test initial state immediately after construction
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Should be disabled (enable=false)
|
|
|
+ EXPECT_EQ(task.getInterval(), 2000); // Should match constructor parameter
|
|
|
+ EXPECT_EQ(task.getIterations(), 10); // Should match constructor parameter
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 0); // No executions yet
|
|
|
+
|
|
|
+ // Test state after enabling (but before execution)
|
|
|
+ task.enable();
|
|
|
+ EXPECT_TRUE(task.isEnabled()); // Should now be enabled
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 0); // Still 0 before first execution
|
|
|
+ // Interval and iterations should remain unchanged
|
|
|
+
|
|
|
+ // Test state after first execution
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 1); // Should increment to 1
|
|
|
+ EXPECT_EQ(task.getIterations(), 9); // Should decrement to 9
|
|
|
+ // Task should still be enabled and interval unchanged
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK CONTROL METHODS TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task enable() and disable() methods with lifecycle callbacks
|
|
|
+ *
|
|
|
+ * TESTS: enable(), disable(), isEnabled()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that enable/disable methods correctly change task state,
|
|
|
+ * trigger appropriate lifecycle callbacks, and properly control task execution.
|
|
|
+ *
|
|
|
+ * CONTROL METHODS TESTED:
|
|
|
+ * - enable(): Activates task for execution, triggers onEnable callback
|
|
|
+ * - disable(): Deactivates task, stops execution, triggers onDisable callback
|
|
|
+ * - isEnabled(): Returns current activation state
|
|
|
+ *
|
|
|
+ * LIFECYCLE CALLBACK BEHAVIOR:
|
|
|
+ * - onEnable(): Called when task transitions from disabled to enabled
|
|
|
+ * - onDisable(): Called when task transitions from enabled to disabled
|
|
|
+ * - Callbacks allow custom logic during state transitions
|
|
|
+ *
|
|
|
+ * TEST SCENARIOS:
|
|
|
+ * 1. Enable disabled task (should trigger onEnable)
|
|
|
+ * 2. Check execution capability (enabled task should execute)
|
|
|
+ * 3. Disable enabled task (should trigger onDisable, stop execution)
|
|
|
+ * 4. Verify no execution after disable
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - State changes should be immediate and accurate
|
|
|
+ * - Lifecycle callbacks should be invoked exactly once per transition
|
|
|
+ * - Execution behavior should match enabled state
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Enable/disable is the primary method for dynamic task control.
|
|
|
+ * This functionality is essential for responsive, event-driven applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskEnableDisable) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 3, &basic_callback, &ts, false);
|
|
|
+
|
|
|
+ // Test enable
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+ task.enable();
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+
|
|
|
+ // Test disable
|
|
|
+ bool prev_state = task.disable();
|
|
|
+ EXPECT_TRUE(prev_state); // Was enabled
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+
|
|
|
+ // Test disable when already disabled
|
|
|
+ prev_state = task.disable();
|
|
|
+ EXPECT_FALSE(prev_state); // Was disabled
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task enableIfNot() conditional enable method
|
|
|
+ *
|
|
|
+ * TESTS: enableIfNot()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that enableIfNot() provides safe conditional enabling,
|
|
|
+ * avoiding redundant state changes and providing status feedback.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - enableIfNot(): Enables task only if currently disabled
|
|
|
+ * - Returns previous enabled state (false = was disabled, true = was enabled)
|
|
|
+ * - Prevents redundant onEnable callback triggers
|
|
|
+ * - Idempotent operation (safe to call multiple times)
|
|
|
+ *
|
|
|
+ * TEST SCENARIOS:
|
|
|
+ * 1. Call on disabled task (should enable and return false)
|
|
|
+ * 2. Call on enabled task (should remain enabled and return true)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - First call: task becomes enabled, returns false (was disabled)
|
|
|
+ * - Second call: task remains enabled, returns true (was already enabled)
|
|
|
+ * - No side effects from redundant calls
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Conditional enabling prevents unnecessary state transitions
|
|
|
+ * and provides application feedback about previous state, useful for
|
|
|
+ * toggle operations and preventing callback storms.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskEnableIfNot) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 1, &basic_callback, &ts, false);
|
|
|
+
|
|
|
+ // Enable when disabled
|
|
|
+ bool was_enabled = task.enableIfNot();
|
|
|
+ EXPECT_FALSE(was_enabled); // Was disabled
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+
|
|
|
+ // Try to enable when already enabled
|
|
|
+ was_enabled = task.enableIfNot();
|
|
|
+ EXPECT_TRUE(was_enabled); // Was already enabled
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task restart() method for resetting task state
|
|
|
+ *
|
|
|
+ * TESTS: restart()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that restart() properly resets task execution state
|
|
|
+ * while maintaining configuration, allowing tasks to run their full
|
|
|
+ * iteration cycle again.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - restart(): Resets iteration counter to original value
|
|
|
+ * - Resets run counter to 0
|
|
|
+ * - Maintains enabled state
|
|
|
+ * - Preserves interval and callback configuration
|
|
|
+ * - Schedules immediate execution (no delay)
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with 3 iterations
|
|
|
+ * 2. Let it execute once (iterations should decrement to 2)
|
|
|
+ * 3. Call restart() (should reset iterations to 3)
|
|
|
+ * 4. Verify state is properly reset
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - After first execution: getIterations() returns 2
|
|
|
+ * - After restart(): getIterations() returns original value (3)
|
|
|
+ * - Task remains enabled throughout
|
|
|
+ * - Ready for immediate re-execution
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Restart functionality enables task recycling and repetitive
|
|
|
+ * workflows without recreating task objects, essential for state machines
|
|
|
+ * and cyclic operations.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskRestart) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 3, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ // Let it run once
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(task.getIterations(), 2); // Should be decremented
|
|
|
+
|
|
|
+ // Restart should reset iterations
|
|
|
+ task.restart();
|
|
|
+ EXPECT_EQ(task.getIterations(), 3); // Reset to original
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task restartDelayed() method for delayed task reset
|
|
|
+ *
|
|
|
+ * TESTS: restartDelayed(delay)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that restartDelayed() resets task state like restart()
|
|
|
+ * but introduces a specified delay before first execution, useful for
|
|
|
+ * timed restart scenarios and spacing between task cycles.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - restartDelayed(delay): Combines restart() with initial delay
|
|
|
+ * - Resets iteration and run counters like restart()
|
|
|
+ * - Schedules first execution after specified delay
|
|
|
+ * - Subsequent executions follow normal interval timing
|
|
|
+ * - Task remains enabled but dormant during delay period
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Execute task once to modify its state
|
|
|
+ * 2. Call restartDelayed(200ms)
|
|
|
+ * 3. Verify no immediate execution (delay period)
|
|
|
+ * 4. Verify execution occurs after delay expires
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Immediate period: no execution despite scheduler calls
|
|
|
+ * - After delay: task executes normally with reset state
|
|
|
+ * - Callback counter increments only after delay period
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Delayed restart enables controlled timing gaps between
|
|
|
+ * task cycles, essential for rate limiting and synchronized multi-task
|
|
|
+ * restart scenarios.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskRestartDelayed) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(50, 2, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ // Let it run once
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ int count_before_restart = callback_counter;
|
|
|
+ task.restartDelayed(200); // Restart with 200ms delay
|
|
|
+
|
|
|
+ // Should not execute immediately
|
|
|
+ delay(50);
|
|
|
+ ts.execute();
|
|
|
+ EXPECT_EQ(callback_counter, count_before_restart);
|
|
|
+
|
|
|
+ // Should execute after delay
|
|
|
+ delay(200);
|
|
|
+ success = runSchedulerUntil(ts, [count_before_restart]() {
|
|
|
+ return callback_counter > count_before_restart;
|
|
|
+ });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK TIMING METHODS TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task delay() method for postponing next execution
|
|
|
+ *
|
|
|
+ * TESTS: delay(milliseconds)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that delay() correctly postpones the next scheduled
|
|
|
+ * execution by the specified amount, without affecting the task's
|
|
|
+ * normal interval timing for subsequent executions.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - delay(ms): Postpones next execution by specified milliseconds
|
|
|
+ * - Affects only the next execution, not the interval permanently
|
|
|
+ * - Task remains enabled during delay period
|
|
|
+ * - After delayed execution, normal interval timing resumes
|
|
|
+ * - Can be called multiple times to accumulate delays
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Execute task once to establish baseline
|
|
|
+ * 2. Call delay(150ms) to postpone next execution
|
|
|
+ * 3. Verify no execution during delay period (50ms < 150ms)
|
|
|
+ * 4. Verify execution occurs after delay expires
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - During delay: callback counter unchanged despite scheduler calls
|
|
|
+ * - After delay: task executes and counter increments
|
|
|
+ * - Subsequent executions follow normal interval
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Dynamic delay allows responsive timing adjustments
|
|
|
+ * based on runtime conditions, essential for adaptive scheduling
|
|
|
+ * and event-driven timing modifications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskDelay) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(50, 5, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ // Let it run once
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ int count_before_delay = callback_counter;
|
|
|
+ task.delay(150); // Delay next execution
|
|
|
+
|
|
|
+ // Should not execute immediately
|
|
|
+ delay(50);
|
|
|
+ ts.execute();
|
|
|
+ EXPECT_EQ(callback_counter, count_before_delay);
|
|
|
+
|
|
|
+ // Should execute after delay
|
|
|
+ delay(150);
|
|
|
+ success = runSchedulerUntil(ts, [count_before_delay]() {
|
|
|
+ return callback_counter > count_before_delay;
|
|
|
+ });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task forceNextIteration() method for immediate execution
|
|
|
+ *
|
|
|
+ * TESTS: forceNextIteration()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that forceNextIteration() bypasses normal interval timing
|
|
|
+ * and schedules the task for immediate execution on the next scheduler pass,
|
|
|
+ * useful for triggering urgent or event-driven task execution.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - forceNextIteration(): Marks task for immediate execution
|
|
|
+ * - Bypasses remaining interval wait time
|
|
|
+ * - Does not affect subsequent interval timing
|
|
|
+ * - Works only if task is enabled
|
|
|
+ * - Executes on very next scheduler.execute() call
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with long interval (1000ms) to prevent natural execution
|
|
|
+ * 2. Let it execute once (starts long interval timer)
|
|
|
+ * 3. Call forceNextIteration() during interval wait
|
|
|
+ * 4. Verify immediate execution on next scheduler pass
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Without force: task would wait full 1000ms interval
|
|
|
+ * - With force: task executes immediately despite interval
|
|
|
+ * - Callback counter increments immediately after force
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Force execution enables responsive event handling and
|
|
|
+ * priority task execution, essential for interrupt-driven scenarios
|
|
|
+ * and urgent task processing.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskForceNextIteration) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(1000, 3, &basic_callback, &ts, true); // Long interval
|
|
|
+
|
|
|
+ // Execute immediately due to enable
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ int count_before_force = callback_counter;
|
|
|
+ task.forceNextIteration();
|
|
|
+
|
|
|
+ // Should execute immediately on next scheduler pass
|
|
|
+ success = runSchedulerUntil(ts, [count_before_force]() {
|
|
|
+ return callback_counter > count_before_force;
|
|
|
+ });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task enableDelayed() method for delayed activation
|
|
|
+ *
|
|
|
+ * TESTS: enableDelayed(delay)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that enableDelayed() enables a task but delays its
|
|
|
+ * first execution by the specified amount, useful for coordinated
|
|
|
+ * task startup and avoiding immediate execution.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - enableDelayed(ms): Enables task but delays first execution
|
|
|
+ * - Task becomes enabled immediately (isEnabled() returns true)
|
|
|
+ * - First execution waits for specified delay period
|
|
|
+ * - After first execution, normal interval timing applies
|
|
|
+ * - Different from enable() which executes immediately
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create disabled task
|
|
|
+ * 2. Call enableDelayed(200ms)
|
|
|
+ * 3. Verify task is enabled but doesn't execute immediately
|
|
|
+ * 4. Verify execution occurs after delay period
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Task enabled: isEnabled() returns true immediately
|
|
|
+ * - During delay: no execution despite scheduler calls
|
|
|
+ * - After delay: task executes normally
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Delayed enabling allows coordinated task startup
|
|
|
+ * sequences and prevents initial execution conflicts, essential
|
|
|
+ * for synchronized multi-task systems.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskEnableDelayed) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 1, &basic_callback, &ts, false);
|
|
|
+
|
|
|
+ task.enableDelayed(200);
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+
|
|
|
+ // Should not execute immediately
|
|
|
+ delay(50);
|
|
|
+ ts.execute();
|
|
|
+ EXPECT_EQ(callback_counter, 0);
|
|
|
+
|
|
|
+ // Should execute after delay
|
|
|
+ delay(200);
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK CONFIGURATION METHODS TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task set() method for complete task reconfiguration
|
|
|
+ *
|
|
|
+ * TESTS: set(interval, iterations, callback, onEnable, onDisable)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that set() completely reconfigures a task with new
|
|
|
+ * parameters, allowing task objects to be reused with different
|
|
|
+ * configurations without recreation.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - set(): Replaces all task configuration parameters
|
|
|
+ * - Updates interval, iterations, callback, and lifecycle callbacks
|
|
|
+ * - Does not change enabled state (task remains in current state)
|
|
|
+ * - Resets internal counters and timing
|
|
|
+ * - Allows complete task repurposing
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create default task (empty configuration)
|
|
|
+ * 2. Use set() to configure with specific parameters
|
|
|
+ * 3. Verify all parameters were set correctly
|
|
|
+ * 4. Verify task remains disabled (default state)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - All set parameters should be retrievable via getter methods
|
|
|
+ * - Task should remain disabled (set() doesn't enable)
|
|
|
+ * - Task ready for enable() to start with new configuration
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Set method enables task object reuse and dynamic
|
|
|
+ * reconfiguration, essential for flexible task management and
|
|
|
+ * memory-efficient applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskSetMethod) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task;
|
|
|
+
|
|
|
+ task.set(300, 7, &basic_callback, &test_onEnable, &test_onDisable);
|
|
|
+
|
|
|
+ EXPECT_EQ(task.getInterval(), 300);
|
|
|
+ EXPECT_EQ(task.getIterations(), 7);
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task setInterval() method for dynamic timing changes
|
|
|
+ *
|
|
|
+ * TESTS: setInterval(newInterval), getInterval()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that setInterval() dynamically changes task execution
|
|
|
+ * timing and that changes take effect for subsequent executions,
|
|
|
+ * enabling adaptive and responsive timing control.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - setInterval(ms): Changes execution interval for future executions
|
|
|
+ * - Does not affect current timing if task is mid-interval
|
|
|
+ * - New interval applies to next scheduled execution
|
|
|
+ * - Can be called multiple times to adjust timing dynamically
|
|
|
+ * - Allows real-time timing optimization
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with initial 100ms interval
|
|
|
+ * 2. Change interval to 500ms and verify getter
|
|
|
+ * 3. Let task execute once with new timing
|
|
|
+ * 4. Change interval to 200ms during execution
|
|
|
+ * 5. Verify next execution uses new 200ms interval
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - getInterval() returns updated value immediately
|
|
|
+ * - Execution timing reflects new interval settings
|
|
|
+ * - Dynamic changes affect subsequent executions
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Dynamic interval adjustment enables adaptive systems
|
|
|
+ * that respond to load, priority, or environmental changes in real-time,
|
|
|
+ * essential for responsive and efficient applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskSetInterval) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 2, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ task.setInterval(500);
|
|
|
+ EXPECT_EQ(task.getInterval(), 500);
|
|
|
+
|
|
|
+ // Interval change should affect timing
|
|
|
+ unsigned long start_time = millis();
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ task.setInterval(200);
|
|
|
+ int count_before = callback_counter;
|
|
|
+ success = runSchedulerUntil(ts, [count_before]() {
|
|
|
+ return callback_counter > count_before;
|
|
|
+ }, 300);
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task setIterations() method for dynamic repetition control
|
|
|
+ *
|
|
|
+ * TESTS: setIterations(newIterations), getIterations()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that setIterations() dynamically changes the number of
|
|
|
+ * remaining executions and that tasks properly auto-disable when reaching
|
|
|
+ * zero iterations, enabling flexible execution count management.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - setIterations(count): Sets number of remaining executions
|
|
|
+ * - Count decrements with each execution
|
|
|
+ * - Task auto-disables when iterations reach zero
|
|
|
+ * - Can extend or reduce remaining executions dynamically
|
|
|
+ * - TASK_FOREVER can be set for infinite execution
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with initial 2 iterations
|
|
|
+ * 2. Change to 5 iterations before execution
|
|
|
+ * 3. Let task run complete cycle (5 executions)
|
|
|
+ * 4. Verify task auto-disables after completing all iterations
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - getIterations() returns updated count
|
|
|
+ * - Task executes exactly 5 times (new iteration count)
|
|
|
+ * - Task automatically disables after final iteration
|
|
|
+ * - Callback counter reaches exactly 5
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Dynamic iteration control allows adaptive execution
|
|
|
+ * cycles based on runtime conditions, essential for conditional
|
|
|
+ * processing and resource-conscious applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskSetIterations) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 2, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ task.setIterations(5);
|
|
|
+ EXPECT_EQ(task.getIterations(), 5);
|
|
|
+
|
|
|
+ // Should run 5 times
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 5; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(callback_counter, 5);
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Should auto-disable after iterations
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task callback switching methods for dynamic behavior
|
|
|
+ *
|
|
|
+ * TESTS: setCallback(), setOnEnable(), setOnDisable()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that callback methods can be dynamically changed during
|
|
|
+ * task lifetime, enabling flexible behavior modification and state-dependent
|
|
|
+ * functionality without task recreation.
|
|
|
+ *
|
|
|
+ * CALLBACK TYPES TESTED:
|
|
|
+ * - setCallback(): Changes main execution callback function
|
|
|
+ * - setOnEnable(): Changes lifecycle callback for enable events
|
|
|
+ * - setOnDisable(): Changes lifecycle callback for disable events
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - Callbacks can be changed while task is running
|
|
|
+ * - New callbacks take effect immediately
|
|
|
+ * - Previous callbacks are completely replaced
|
|
|
+ * - Lifecycle callbacks trigger during state transitions
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with callback_1 function
|
|
|
+ * 2. Switch to callback_2 and verify execution uses new callback
|
|
|
+ * 3. Set lifecycle callbacks and verify they trigger during state changes
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Execution produces output from new callback (callback_2)
|
|
|
+ * - Enable transition triggers onEnable callback
|
|
|
+ * - Disable transition triggers onDisable callback
|
|
|
+ * - All callback switches take effect immediately
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Dynamic callback switching enables state machines,
|
|
|
+ * adaptive behavior, and multi-phase task processing without
|
|
|
+ * complex conditional logic in callbacks.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskSetCallbacks) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 2, &callback_1, &ts, false);
|
|
|
+
|
|
|
+ // Test setCallback
|
|
|
+ task.setCallback(&callback_2);
|
|
|
+ task.enable();
|
|
|
+
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(getTestOutput(0), "callback_2");
|
|
|
+
|
|
|
+ // Test setOnEnable and setOnDisable
|
|
|
+ task.setOnEnable(&test_onEnable);
|
|
|
+ task.setOnDisable(&test_onDisable);
|
|
|
+
|
|
|
+ task.disable();
|
|
|
+ task.enable();
|
|
|
+
|
|
|
+ EXPECT_TRUE(onEnable_called);
|
|
|
+ task.disable();
|
|
|
+ EXPECT_TRUE(onDisable_called);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK ITERATION STATE TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task iteration state query methods for execution context
|
|
|
+ *
|
|
|
+ * TESTS: isFirstIteration(), isLastIteration()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that iteration state query methods correctly identify
|
|
|
+ * the execution context within a task's iteration cycle, enabling
|
|
|
+ * conditional logic based on iteration position.
|
|
|
+ *
|
|
|
+ * STATE METHODS TESTED:
|
|
|
+ * - isFirstIteration(): Returns true only during the first execution
|
|
|
+ * - isLastIteration(): Returns true only during the final execution
|
|
|
+ * - Both provide context for conditional callback behavior
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with 3 iterations
|
|
|
+ * 2. Execute first iteration (implicit due to enable=true)
|
|
|
+ * 3. Use dynamic callback to check states during iterations 2 and 3
|
|
|
+ * 4. Verify correct state reporting for middle and final iterations
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - First iteration: isFirstIteration()=true, isLastIteration()=false
|
|
|
+ * - Middle iteration: isFirstIteration()=false, isLastIteration()=false
|
|
|
+ * - Final iteration: isFirstIteration()=false, isLastIteration()=true
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Iteration state queries enable initialization and cleanup
|
|
|
+ * logic within callbacks, essential for resource management and
|
|
|
+ * conditional processing based on execution phase.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskIterationState) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 3, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ // First iteration
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ // After first execution, check states in next callback
|
|
|
+ task.setCallback([&task]() {
|
|
|
+ callback_counter++;
|
|
|
+ if (callback_counter == 2) {
|
|
|
+ EXPECT_FALSE(task.isFirstIteration()); // Not first anymore
|
|
|
+ EXPECT_FALSE(task.isLastIteration()); // Not last yet
|
|
|
+ } else if (callback_counter == 3) {
|
|
|
+ EXPECT_FALSE(task.isFirstIteration()); // Not first
|
|
|
+ EXPECT_TRUE(task.isLastIteration()); // This is last
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ // Run remaining iterations
|
|
|
+ success = runSchedulerUntil(ts, []() { return callback_counter >= 3; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK CALLBACK SWITCHING TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task yield() method for dynamic callback switching
|
|
|
+ *
|
|
|
+ * TESTS: yield(newCallback)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that yield() permanently switches the task's callback
|
|
|
+ * function to a new function, enabling state machine behavior and
|
|
|
+ * multi-phase task processing within a single task object.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - yield(callback): Permanently changes task's callback function
|
|
|
+ * - Switch takes effect immediately
|
|
|
+ * - All subsequent executions use the new callback
|
|
|
+ * - Original callback is completely replaced
|
|
|
+ * - Enables state machine and multi-step processing patterns
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with multi_step_callback_1
|
|
|
+ * 2. In first execution, call yield() to switch to multi_step_callback_2
|
|
|
+ * 3. Verify first execution produces "step_1" output
|
|
|
+ * 4. Verify second execution produces "step_2" output (new callback)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - First execution: "step_1" output from original callback
|
|
|
+ * - Yield call: switches to multi_step_callback_2
|
|
|
+ * - Second execution: "step_2" output from new callback
|
|
|
+ * - All future executions use new callback
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Yield enables complex state machines and multi-phase
|
|
|
+ * processing within single tasks, essential for sequential operations
|
|
|
+ * and adaptive task behavior.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskYield) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(200, 3, &multi_step_callback_1, &ts, true);
|
|
|
+
|
|
|
+ // Modify callback to test yield
|
|
|
+ task.setCallback([&task]() {
|
|
|
+ test_output.push_back("step_1");
|
|
|
+ task.yield(&multi_step_callback_2);
|
|
|
+ });
|
|
|
+
|
|
|
+ // First execution
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(getTestOutput(0), "step_1");
|
|
|
+
|
|
|
+ // Should immediately execute step 2
|
|
|
+ success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(getTestOutput(1), "step_2");
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task yieldOnce() method for single callback switch
|
|
|
+ *
|
|
|
+ * TESTS: yieldOnce(newCallback)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that yieldOnce() switches to a new callback for exactly
|
|
|
+ * one execution, then automatically disables the task, enabling one-time
|
|
|
+ * completion or finalization logic.
|
|
|
+ *
|
|
|
+ * METHOD BEHAVIOR:
|
|
|
+ * - yieldOnce(callback): Switches to new callback for one execution only
|
|
|
+ * - New callback executes exactly once on next scheduler pass
|
|
|
+ * - Task automatically disables after the single execution
|
|
|
+ * - Original callback is not restored (task becomes inactive)
|
|
|
+ * - Useful for completion/cleanup logic and one-shot operations
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with 5 iterations and initial callback
|
|
|
+ * 2. In first execution, call yieldOnce() to switch to completion callback
|
|
|
+ * 3. Verify first execution produces "step_1" output
|
|
|
+ * 4. Verify second execution produces "step_2" output
|
|
|
+ * 5. Verify task automatically disables after yieldOnce execution
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - First execution: "step_1" from original callback with yieldOnce call
|
|
|
+ * - Second execution: "step_2" from yielded callback
|
|
|
+ * - After second execution: task becomes disabled automatically
|
|
|
+ * - No further executions occur
|
|
|
+ *
|
|
|
+ * IMPORTANCE: YieldOnce enables one-time finalization, cleanup, and
|
|
|
+ * completion logic, essential for graceful task termination and
|
|
|
+ * resource cleanup patterns.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskYieldOnce) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 5, &multi_step_callback_1, &ts, true);
|
|
|
+
|
|
|
+ // First execution - yield once to step 2
|
|
|
+ task.setCallback([&task]() {
|
|
|
+ test_output.push_back("step_1");
|
|
|
+ task.yieldOnce(&multi_step_callback_2);
|
|
|
+ });
|
|
|
+
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ // Should execute step 2 once then disable
|
|
|
+ success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(getTestOutput(1), "step_2");
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Should be disabled after yieldOnce
|
|
|
+}
|
|
|
+
|
|
|
+// ================== TASK ONENABLE/ONDISABLE TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task OnEnable/OnDisable lifecycle callback behavior
|
|
|
+ *
|
|
|
+ * TESTS: onEnable callback, onDisable callback, lifecycle triggers
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that lifecycle callbacks are properly invoked during
|
|
|
+ * task state transitions, enabling initialization and cleanup logic
|
|
|
+ * to execute at appropriate times.
|
|
|
+ *
|
|
|
+ * LIFECYCLE CALLBACK BEHAVIOR:
|
|
|
+ * - onEnable(): Called when task transitions from disabled to enabled
|
|
|
+ * - onDisable(): Called when task transitions from enabled to disabled
|
|
|
+ * - Callbacks execute synchronously during state change
|
|
|
+ * - Enable callback can prevent enabling by returning false
|
|
|
+ * - Disable callback is informational (no return value)
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with lifecycle callbacks but start disabled
|
|
|
+ * 2. Enable task and verify onEnable callback is invoked
|
|
|
+ * 3. Disable task and verify onDisable callback is invoked
|
|
|
+ * 4. Verify callbacks are triggered exactly once per transition
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - OnEnable called during enable() transition
|
|
|
+ * - OnDisable called during disable() transition
|
|
|
+ * - Global callback flags properly set
|
|
|
+ * - Task state changes successful
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Lifecycle callbacks enable proper resource management,
|
|
|
+ * initialization, and cleanup, essential for robust task lifecycle
|
|
|
+ * management and preventing resource leaks.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskOnEnableOnDisable) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 2, &basic_callback, &ts, false, &test_onEnable, &test_onDisable);
|
|
|
+
|
|
|
+ // Test OnEnable
|
|
|
+ task.enable();
|
|
|
+ EXPECT_TRUE(onEnable_called);
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+
|
|
|
+ // Reset and test OnDisable
|
|
|
+ onEnable_called = false;
|
|
|
+ task.disable();
|
|
|
+ EXPECT_TRUE(onDisable_called);
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Task OnEnable callback returning false to prevent enabling
|
|
|
+ *
|
|
|
+ * TESTS: onEnable callback return value, conditional enabling
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that when an onEnable callback returns false, the task
|
|
|
+ * remains disabled, providing a mechanism for conditional enabling based
|
|
|
+ * on runtime conditions or resource availability.
|
|
|
+ *
|
|
|
+ * CONDITIONAL ENABLING BEHAVIOR:
|
|
|
+ * - onEnable(): Must return boolean (true = allow enable, false = prevent)
|
|
|
+ * - When returns false: task remains disabled despite enable() call
|
|
|
+ * - When returns true: task becomes enabled normally
|
|
|
+ * - Callback is always invoked (for logging/monitoring purposes)
|
|
|
+ * - Enables conditional logic for resource-dependent tasks
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with onEnable callback that returns false
|
|
|
+ * 2. Call enable() on the task
|
|
|
+ * 3. Verify onEnable callback was invoked
|
|
|
+ * 4. Verify task remains disabled (enable() call rejected)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - OnEnable callback is called (onEnable_called flag set)
|
|
|
+ * - Task remains disabled (isEnabled() returns false)
|
|
|
+ * - Enable attempt is gracefully rejected
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Conditional enabling prevents tasks from starting when
|
|
|
+ * prerequisites aren't met, essential for resource-dependent operations
|
|
|
+ * and safety-critical system states.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskOnEnableReturnsFalse) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 1, &basic_callback, &ts, false, &test_onEnable_false, &test_onDisable);
|
|
|
+
|
|
|
+ // OnEnable returns false, task should remain disabled
|
|
|
+ task.enable();
|
|
|
+ EXPECT_TRUE(onEnable_called);
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Should remain disabled
|
|
|
+}
|
|
|
+
|
|
|
+// ================== SCHEDULER CONSTRUCTOR AND INIT TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler constructor and basic operation
|
|
|
+ *
|
|
|
+ * TESTS: Scheduler(), execute() with no tasks
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that a Scheduler object can be constructed successfully
|
|
|
+ * and can safely execute even when no tasks are registered, demonstrating
|
|
|
+ * robustness in edge cases.
|
|
|
+ *
|
|
|
+ * CONSTRUCTOR BEHAVIOR:
|
|
|
+ * - Scheduler(): Creates empty scheduler with no tasks
|
|
|
+ * - execute(): Safely handles empty task list
|
|
|
+ * - No exceptions or crashes in degenerate cases
|
|
|
+ * - Ready to accept tasks via Task constructor registration
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create empty scheduler
|
|
|
+ * 2. Call execute() on empty scheduler
|
|
|
+ * 3. Verify no output generated (no tasks to execute)
|
|
|
+ * 4. Verify no crashes or exceptions
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Constructor succeeds without exceptions
|
|
|
+ * - Execute runs safely with no tasks
|
|
|
+ * - No test output produced (counter remains 0)
|
|
|
+ * - Scheduler ready for task registration
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Empty scheduler robustness ensures safe operation during
|
|
|
+ * initialization phases and prevents crashes in edge cases, essential
|
|
|
+ * for reliable system startup sequences.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerConstructor) {
|
|
|
+ Scheduler ts;
|
|
|
+
|
|
|
+ // Should be able to execute without tasks
|
|
|
+ ts.execute();
|
|
|
+ EXPECT_EQ(getTestOutputCount(), 0);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler init() method for reinitialization
|
|
|
+ *
|
|
|
+ * TESTS: init()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that init() properly reinitializes the scheduler
|
|
|
+ * state while preserving task registrations, enabling scheduler
|
|
|
+ * reset without losing configured tasks.
|
|
|
+ *
|
|
|
+ * INIT METHOD BEHAVIOR:
|
|
|
+ * - init(): Reinitializes internal scheduler state
|
|
|
+ * - Preserves registered tasks and their configurations
|
|
|
+ * - Resets timing and execution state
|
|
|
+ * - Tasks remain functional after reinitialization
|
|
|
+ * - Useful for system restart scenarios
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create scheduler with one registered task
|
|
|
+ * 2. Call init() to reinitialize scheduler
|
|
|
+ * 3. Verify task still executes properly after init
|
|
|
+ * 4. Confirm scheduler functionality is preserved
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Init() completes without errors
|
|
|
+ * - Task remains registered and functional
|
|
|
+ * - Task executes successfully after reinitialization
|
|
|
+ * - Callback counter increments as expected
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Scheduler reinitialization enables system restart
|
|
|
+ * scenarios and state recovery while preserving task configurations,
|
|
|
+ * essential for robust and recoverable applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerInit) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 1, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ ts.init(); // Should reinitialize
|
|
|
+
|
|
|
+ // Task should still work after init
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== SCHEDULER TASK MANAGEMENT TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler addTask() and deleteTask() methods for manual task management
|
|
|
+ *
|
|
|
+ * TESTS: addTask(task), deleteTask(task)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that tasks can be manually added to and removed from
|
|
|
+ * schedulers, enabling dynamic task management and scheduler composition
|
|
|
+ * patterns beyond automatic constructor registration.
|
|
|
+ *
|
|
|
+ * TASK MANAGEMENT BEHAVIOR:
|
|
|
+ * - addTask(): Manually registers task with scheduler
|
|
|
+ * - deleteTask(): Removes task from scheduler's management
|
|
|
+ * - Tasks can be added without using scheduler parameter in constructor
|
|
|
+ * - Deleted tasks are no longer executed by scheduler
|
|
|
+ * - Remaining tasks continue normal operation
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create tasks without scheduler assignment (nullptr)
|
|
|
+ * 2. Manually add both tasks to scheduler
|
|
|
+ * 3. Enable and execute both tasks
|
|
|
+ * 4. Delete one task from scheduler
|
|
|
+ * 5. Verify only remaining task executes
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Both tasks execute when added to scheduler
|
|
|
+ * - After deletion, only non-deleted task executes
|
|
|
+ * - Deleted task becomes unmanaged (won't execute)
|
|
|
+ * - Scheduler continues operating with remaining tasks
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Manual task management enables dynamic scheduler composition,
|
|
|
+ * conditional task registration, and runtime task lifecycle management,
|
|
|
+ * essential for flexible and adaptive applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerAddDeleteTask) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task1(100, 1, &callback_1, nullptr, false);
|
|
|
+ Task task2(150, 1, &callback_2, nullptr, false);
|
|
|
+
|
|
|
+ // Add tasks manually
|
|
|
+ ts.addTask(task1);
|
|
|
+ ts.addTask(task2);
|
|
|
+
|
|
|
+ task1.enable();
|
|
|
+ task2.enable();
|
|
|
+
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ // Delete task1
|
|
|
+ ts.deleteTask(task1);
|
|
|
+
|
|
|
+ // Only task2 should be manageable now
|
|
|
+ clearTestOutput();
|
|
|
+ task2.restart();
|
|
|
+ success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(getTestOutput(0), "callback_2");
|
|
|
+}
|
|
|
+
|
|
|
+// ================== SCHEDULER EXECUTION CONTROL TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler execute() method return value for idle detection
|
|
|
+ *
|
|
|
+ * TESTS: execute() return value, idle state detection
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that execute() correctly returns idle status, enabling
|
|
|
+ * applications to detect when no tasks are ready for execution and
|
|
|
+ * implement power management or alternative processing.
|
|
|
+ *
|
|
|
+ * EXECUTE RETURN VALUE BEHAVIOR:
|
|
|
+ * - execute(): Returns false when tasks executed, true when idle
|
|
|
+ * - Idle = no tasks ready for execution at current time
|
|
|
+ * - Non-idle = one or more tasks executed during call
|
|
|
+ * - Enables power management and conditional processing
|
|
|
+ * - Critical for battery-powered and real-time applications
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create two tasks with different intervals (100ms, 150ms)
|
|
|
+ * 2. First execute() should be non-idle (immediate execution)
|
|
|
+ * 3. Run until both tasks complete
|
|
|
+ * 4. Later execute() should be idle (no tasks ready)
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - First execute(): returns false (tasks executed)
|
|
|
+ * - Both tasks produce expected output
|
|
|
+ * - After completion: execute() returns true (idle)
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Idle detection enables efficient resource utilization,
|
|
|
+ * power management, and responsive application behavior, essential
|
|
|
+ * for embedded and battery-powered systems.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerExecute) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task1(100, 1, &callback_1, &ts, true);
|
|
|
+ Task task2(150, 1, &callback_2, &ts, true);
|
|
|
+
|
|
|
+ bool idle = ts.execute();
|
|
|
+ // First execute should run task1 immediately, so not idle
|
|
|
+ EXPECT_FALSE(idle);
|
|
|
+
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ // After tasks complete, should be idle
|
|
|
+ delay(200);
|
|
|
+ idle = ts.execute();
|
|
|
+ EXPECT_TRUE(idle);
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler enableAll() and disableAll() methods for bulk control
|
|
|
+ *
|
|
|
+ * TESTS: enableAll(), disableAll()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that enableAll() and disableAll() methods correctly
|
|
|
+ * change the state of all tasks managed by the scheduler, enabling
|
|
|
+ * coordinated system-wide task control.
|
|
|
+ *
|
|
|
+ * BULK CONTROL BEHAVIOR:
|
|
|
+ * - enableAll(): Enables every task registered with scheduler
|
|
|
+ * - disableAll(): Disables every task registered with scheduler
|
|
|
+ * - Affects all tasks regardless of current state
|
|
|
+ * - Triggers onEnable/onDisable callbacks for each task
|
|
|
+ * - Enables system-wide start/stop functionality
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create three tasks in disabled state
|
|
|
+ * 2. Call enableAll() and verify all tasks become enabled
|
|
|
+ * 3. Call disableAll() and verify all tasks become disabled
|
|
|
+ * 4. Confirm state changes affect all registered tasks
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - EnableAll(): all tasks report isEnabled() = true
|
|
|
+ * - DisableAll(): all tasks report isEnabled() = false
|
|
|
+ * - State changes are consistent across all tasks
|
|
|
+ * - Bulk operations affect entire task set
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Bulk enable/disable enables system-wide control patterns,
|
|
|
+ * emergency stops, coordinated startup/shutdown, and mode switching,
|
|
|
+ * essential for complex multi-task systems.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerEnableDisableAll) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task1(100, 1, &callback_1, &ts, false);
|
|
|
+ Task task2(150, 1, &callback_2, &ts, false);
|
|
|
+ Task task3(200, 1, &callback_3, &ts, false);
|
|
|
+
|
|
|
+ // Enable all
|
|
|
+ ts.enableAll();
|
|
|
+ EXPECT_TRUE(task1.isEnabled());
|
|
|
+ EXPECT_TRUE(task2.isEnabled());
|
|
|
+ EXPECT_TRUE(task3.isEnabled());
|
|
|
+
|
|
|
+ // Disable all
|
|
|
+ ts.disableAll();
|
|
|
+ EXPECT_FALSE(task1.isEnabled());
|
|
|
+ EXPECT_FALSE(task2.isEnabled());
|
|
|
+ EXPECT_FALSE(task3.isEnabled());
|
|
|
+}
|
|
|
+
|
|
|
+// ================== SCHEDULER TIME QUERY TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler timeUntilNextIteration() method for timing queries
|
|
|
+ *
|
|
|
+ * TESTS: timeUntilNextIteration(task)
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that timeUntilNextIteration() accurately reports the
|
|
|
+ * remaining time before a task's next scheduled execution, enabling
|
|
|
+ * predictive scheduling and timing-based application logic.
|
|
|
+ *
|
|
|
+ * TIME QUERY BEHAVIOR:
|
|
|
+ * - timeUntilNextIteration(): Returns milliseconds until next execution
|
|
|
+ * - Returns -1 for disabled tasks (not scheduled)
|
|
|
+ * - Returns positive value for enabled tasks with pending execution
|
|
|
+ * - Value decreases as time passes toward execution
|
|
|
+ * - Enables predictive timing and coordination
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Check disabled task (should return -1)
|
|
|
+ * 2. Enable task with 500ms delay
|
|
|
+ * 3. Verify time remaining is close to 500ms
|
|
|
+ * 4. Wait 200ms and verify time decreased appropriately
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Disabled task: returns -1
|
|
|
+ * - Initially delayed task: returns ~500ms
|
|
|
+ * - After 200ms delay: returns ~300ms
|
|
|
+ * - Timing accuracy within reasonable bounds
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Time queries enable predictive scheduling, coordination
|
|
|
+ * between tasks, and timing-based application logic, essential for
|
|
|
+ * real-time and synchronized operations.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerTimeUntilNextIteration) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(1000, 1, &basic_callback, &ts, false);
|
|
|
+
|
|
|
+ // Disabled task should return -1
|
|
|
+ long time_until = ts.timeUntilNextIteration(task);
|
|
|
+ EXPECT_EQ(time_until, -1);
|
|
|
+
|
|
|
+ // Enabled task with delay
|
|
|
+ task.enableDelayed(500);
|
|
|
+ time_until = ts.timeUntilNextIteration(task);
|
|
|
+ EXPECT_GT(time_until, 400); // Should be close to 500ms
|
|
|
+ EXPECT_LE(time_until, 500);
|
|
|
+
|
|
|
+ // After some time passes
|
|
|
+ delay(200);
|
|
|
+ time_until = ts.timeUntilNextIteration(task);
|
|
|
+ EXPECT_GT(time_until, 200); // Should be around 300ms
|
|
|
+ EXPECT_LE(time_until, 300);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== SCHEDULER TASK ACCESS TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler getCurrentTask() method for callback context identification
|
|
|
+ *
|
|
|
+ * TESTS: getCurrentTask()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that getCurrentTask() correctly returns a pointer to the
|
|
|
+ * currently executing task when called from within a task callback,
|
|
|
+ * enabling self-referential task operations and context awareness.
|
|
|
+ *
|
|
|
+ * CONTEXT ACCESS BEHAVIOR:
|
|
|
+ * - getCurrentTask(): Returns pointer to currently executing task
|
|
|
+ * - Only valid when called from within task callback
|
|
|
+ * - Returns nullptr when called outside task execution context
|
|
|
+ * - Enables task self-modification and context-aware operations
|
|
|
+ * - Critical for advanced task patterns and introspection
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with lambda callback that captures getCurrentTask()
|
|
|
+ * 2. Execute task and capture the returned task pointer
|
|
|
+ * 3. Verify returned pointer matches the actual task object
|
|
|
+ * 4. Confirm context identification works correctly
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - getCurrentTask() returns valid pointer during execution
|
|
|
+ * - Returned pointer equals address of actual task object
|
|
|
+ * - Context identification is accurate and reliable
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Current task access enables advanced patterns like
|
|
|
+ * task self-modification, recursive operations, and context-aware
|
|
|
+ * callback behavior, essential for sophisticated task orchestration.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerCurrentTask) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task* current_task_ptr = nullptr;
|
|
|
+
|
|
|
+ Task task(100, 1, [&ts, ¤t_task_ptr]() {
|
|
|
+ current_task_ptr = ts.getCurrentTask();
|
|
|
+ test_output.push_back("got_current_task");
|
|
|
+ }, &ts, true);
|
|
|
+
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 1; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(current_task_ptr, &task);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== SCHEDULER TIMING CONTROL TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Test Scheduler startNow() method for immediate execution trigger
|
|
|
+ *
|
|
|
+ * TESTS: startNow()
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that startNow() forces all enabled tasks to execute
|
|
|
+ * immediately on the next scheduler pass, regardless of their current
|
|
|
+ * timing state, enabling synchronized restart and emergency execution.
|
|
|
+ *
|
|
|
+ * START NOW BEHAVIOR:
|
|
|
+ * - startNow(): Forces immediate execution of all enabled tasks
|
|
|
+ * - Bypasses all interval and delay timing
|
|
|
+ * - Affects all tasks managed by scheduler
|
|
|
+ * - Useful for synchronized restart and emergency execution
|
|
|
+ * - Tasks resume normal timing after startNow execution
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create tasks with long intervals that would normally delay execution
|
|
|
+ * 2. Let them execute once, then restart with delays
|
|
|
+ * 3. Call startNow() to bypass delays
|
|
|
+ * 4. Verify all tasks execute immediately despite delays
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Initial execution: tasks run due to enable()
|
|
|
+ * - After restart+delay: tasks would normally wait
|
|
|
+ * - After startNow(): all tasks execute immediately
|
|
|
+ * - All expected outputs are produced
|
|
|
+ *
|
|
|
+ * IMPORTANCE: StartNow enables synchronized system restart, emergency
|
|
|
+ * execution, and coordinated task triggering, essential for real-time
|
|
|
+ * systems and emergency response scenarios.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, SchedulerStartNow) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task1(1000, 1, &callback_1, &ts, true); // Long interval
|
|
|
+ Task task2(2000, 1, &callback_2, &ts, true); // Even longer interval
|
|
|
+
|
|
|
+ // Tasks should execute immediately due to enable(), let them
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ clearTestOutput();
|
|
|
+
|
|
|
+ // Restart tasks with long intervals
|
|
|
+ task1.restart();
|
|
|
+ task2.restart();
|
|
|
+ task1.delay(1000); // Delay them
|
|
|
+ task2.delay(2000);
|
|
|
+
|
|
|
+ // startNow should make them execute immediately
|
|
|
+ ts.startNow();
|
|
|
+ success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 2; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+}
|
|
|
+
|
|
|
+// ================== INTEGRATION TESTS ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Integration test for complete task lifecycle with all features
|
|
|
+ *
|
|
|
+ * TESTS: Full task lifecycle including enable, execute, auto-disable, restart
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that all task features work together correctly in a
|
|
|
+ * realistic usage scenario, testing the integration of multiple methods
|
|
|
+ * and lifecycle events in sequence.
|
|
|
+ *
|
|
|
+ * LIFECYCLE INTEGRATION TESTED:
|
|
|
+ * - Initial state verification (disabled, zero counters)
|
|
|
+ * - Enable with onEnable callback triggering
|
|
|
+ * - Multiple executions with proper counter tracking
|
|
|
+ * - Auto-disable after completing all iterations
|
|
|
+ * - OnDisable callback triggering after completion
|
|
|
+ * - Restart functionality with state reset
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with 3 iterations and lifecycle callbacks
|
|
|
+ * 2. Verify initial disabled state
|
|
|
+ * 3. Enable and verify onEnable callback
|
|
|
+ * 4. Execute all 3 iterations and verify counters
|
|
|
+ * 5. Verify auto-disable and onDisable callback
|
|
|
+ * 6. Restart and verify reset state and re-enable
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - All lifecycle callbacks triggered at correct times
|
|
|
+ * - Execution and iteration counters track correctly
|
|
|
+ * - Auto-disable occurs after completing iterations
|
|
|
+ * - Restart properly resets state and re-enables
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Integration testing validates that all features work
|
|
|
+ * together as designed, ensuring reliable operation in real applications
|
|
|
+ * with complex task lifecycle requirements.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, ComplexTaskLifecycle) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(200, 3, &basic_callback, &ts, false, &test_onEnable, &test_onDisable);
|
|
|
+
|
|
|
+ // Full lifecycle test
|
|
|
+ EXPECT_FALSE(task.isEnabled());
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 0);
|
|
|
+
|
|
|
+ // Enable and run
|
|
|
+ task.enable();
|
|
|
+ EXPECT_TRUE(onEnable_called);
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+
|
|
|
+ // Run all iterations
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 3; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_EQ(callback_counter, 3);
|
|
|
+ EXPECT_EQ(task.getRunCounter(), 3);
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Auto-disabled after iterations
|
|
|
+ EXPECT_TRUE(onDisable_called);
|
|
|
+
|
|
|
+ // Restart and verify
|
|
|
+ onEnable_called = false;
|
|
|
+ onDisable_called = false;
|
|
|
+ task.restart();
|
|
|
+ EXPECT_TRUE(onEnable_called);
|
|
|
+ EXPECT_TRUE(task.isEnabled());
|
|
|
+ EXPECT_EQ(task.getIterations(), 3); // Reset
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Integration test for multiple concurrent tasks with different timing
|
|
|
+ *
|
|
|
+ * TESTS: Multiple tasks with different enable timing and execution patterns
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that the scheduler correctly manages multiple concurrent
|
|
|
+ * tasks with different timing characteristics, ensuring proper execution
|
|
|
+ * order and no interference between tasks.
|
|
|
+ *
|
|
|
+ * MULTI-TASK INTEGRATION TESTED:
|
|
|
+ * - Three tasks with different intervals (100ms, 150ms, 200ms)
|
|
|
+ * - Different enable timing: immediate, 50ms delay, 100ms delay
|
|
|
+ * - Each task executes 2 iterations (6 total executions)
|
|
|
+ * - Proper execution ordering based on timing
|
|
|
+ * - No task interference or missed executions
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create three tasks with different intervals and 2 iterations each
|
|
|
+ * 2. Enable task1 immediately, task2 after 50ms, task3 after 100ms
|
|
|
+ * 3. Run until all 6 executions complete (2 per task)
|
|
|
+ * 4. Verify first execution follows enable timing order
|
|
|
+ * 5. Confirm all tasks complete their iterations
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - All 6 executions complete successfully
|
|
|
+ * - First execution is from task1 (immediate enable)
|
|
|
+ * - Total execution count reaches 6
|
|
|
+ * - No tasks interfere with each other
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Multi-task integration validates scheduler's core
|
|
|
+ * functionality under realistic concurrent workloads, ensuring
|
|
|
+ * reliable operation in complex applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, MultipleTasksInteraction) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task1(100, 2, &callback_1, &ts, false);
|
|
|
+ Task task2(150, 2, &callback_2, &ts, false);
|
|
|
+ Task task3(200, 2, &callback_3, &ts, false);
|
|
|
+
|
|
|
+ // Enable with different timings
|
|
|
+ task1.enable();
|
|
|
+ task2.enableDelayed(50);
|
|
|
+ task3.enableDelayed(100);
|
|
|
+
|
|
|
+ // All should execute their iterations
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return getTestOutputCount() >= 6; });
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+
|
|
|
+ // Verify execution order (first executions should follow timing)
|
|
|
+ EXPECT_EQ(getTestOutput(0), "callback_1"); // Immediate
|
|
|
+ // Subsequent order may vary due to intervals
|
|
|
+}
|
|
|
+
|
|
|
+// ================== EDGE CASES AND ERROR HANDLING ==================
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Edge case test for task with zero iterations
|
|
|
+ *
|
|
|
+ * TESTS: Task behavior with 0 iterations
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that tasks created with zero iterations behave correctly
|
|
|
+ * by not executing and remaining disabled, ensuring safe handling of
|
|
|
+ * degenerate iteration counts.
|
|
|
+ *
|
|
|
+ * ZERO ITERATIONS BEHAVIOR:
|
|
|
+ * - Task with 0 iterations should never execute
|
|
|
+ * - Task should be automatically disabled (no valid executions)
|
|
|
+ * - Scheduler should safely handle such tasks without errors
|
|
|
+ * - Useful for conditional task creation patterns
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with 0 iterations but enabled=true
|
|
|
+ * 2. Run scheduler multiple times
|
|
|
+ * 3. Verify no executions occur
|
|
|
+ * 4. Verify task becomes/remains disabled
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Callback counter remains 0 (no executions)
|
|
|
+ * - Task is disabled (invalid iteration count)
|
|
|
+ * - No errors or crashes occur
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Zero iteration handling prevents invalid execution
|
|
|
+ * states and enables conditional task creation patterns, essential
|
|
|
+ * for robust edge case handling.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskZeroIterations) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 0, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ // Should not execute with 0 iterations
|
|
|
+ delay(200);
|
|
|
+ ts.execute();
|
|
|
+ EXPECT_EQ(callback_counter, 0);
|
|
|
+ EXPECT_FALSE(task.isEnabled()); // Should be disabled
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Edge case test for task with infinite iterations (TASK_FOREVER)
|
|
|
+ *
|
|
|
+ * TESTS: Task behavior with TASK_FOREVER iterations
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that tasks configured with TASK_FOREVER execute
|
|
|
+ * indefinitely without auto-disabling, maintaining consistent behavior
|
|
|
+ * for infinite execution scenarios.
|
|
|
+ *
|
|
|
+ * INFINITE ITERATIONS BEHAVIOR:
|
|
|
+ * - TASK_FOREVER (-1) indicates infinite iterations
|
|
|
+ * - Task never auto-disables due to iteration count
|
|
|
+ * - getIterations() continues returning TASK_FOREVER
|
|
|
+ * - Executions continue until manually disabled
|
|
|
+ * - Essential for background and monitoring tasks
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with TASK_FOREVER iterations
|
|
|
+ * 2. Run scheduler until multiple executions occur
|
|
|
+ * 3. Verify task remains enabled throughout
|
|
|
+ * 4. Verify getIterations() still returns TASK_FOREVER
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - Task executes at least 5 times within timeout
|
|
|
+ * - Task remains enabled after multiple executions
|
|
|
+ * - getIterations() continues returning TASK_FOREVER
|
|
|
+ * - No auto-disable occurs
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Infinite iteration support enables background tasks,
|
|
|
+ * monitoring loops, and continuous processing, essential for
|
|
|
+ * long-running and service-oriented applications.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskInfiniteIterations) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(50, TASK_FOREVER, &basic_callback, &ts, true);
|
|
|
+
|
|
|
+ // Should keep running indefinitely
|
|
|
+ bool success = runSchedulerUntil(ts, []() { return callback_counter >= 5; }, 400);
|
|
|
+ EXPECT_TRUE(success);
|
|
|
+ EXPECT_TRUE(task.isEnabled()); // Should still be enabled
|
|
|
+ EXPECT_EQ(task.getIterations(), TASK_FOREVER); // Should remain -1
|
|
|
+}
|
|
|
+
|
|
|
+/**
|
|
|
+ * @brief Edge case test for task with null callback pointer
|
|
|
+ *
|
|
|
+ * TESTS: Task behavior with nullptr callback
|
|
|
+ *
|
|
|
+ * PURPOSE: Verify that tasks created with null callback pointers handle
|
|
|
+ * execution gracefully without crashing, demonstrating robustness in
|
|
|
+ * edge cases and enabling placeholder task patterns.
|
|
|
+ *
|
|
|
+ * NULL CALLBACK BEHAVIOR:
|
|
|
+ * - Task with nullptr callback should not crash during execution
|
|
|
+ * - Task lifecycle continues normally (timing, iterations, etc.)
|
|
|
+ * - No callback code executes (safe no-op behavior)
|
|
|
+ * - Enables placeholder and template task patterns
|
|
|
+ * - Demonstrates scheduler robustness
|
|
|
+ *
|
|
|
+ * TEST SCENARIO:
|
|
|
+ * 1. Create task with nullptr callback but valid parameters
|
|
|
+ * 2. Run scheduler and let task execute
|
|
|
+ * 3. Verify no crashes or exceptions occur
|
|
|
+ * 4. Verify no callback-specific effects occur
|
|
|
+ *
|
|
|
+ * EXPECTATIONS:
|
|
|
+ * - No crashes or exceptions during execution
|
|
|
+ * - Callback counter remains 0 (no callback executed)
|
|
|
+ * - Task lifecycle proceeds normally
|
|
|
+ * - Scheduler continues operating safely
|
|
|
+ *
|
|
|
+ * IMPORTANCE: Null callback handling ensures scheduler robustness
|
|
|
+ * and enables placeholder task patterns, essential for template
|
|
|
+ * systems and defensive programming practices.
|
|
|
+ */
|
|
|
+TEST_F(SchedulerThoroughTest, TaskNullCallback) {
|
|
|
+ Scheduler ts;
|
|
|
+ Task task(100, 3, nullptr, &ts, true);
|
|
|
+
|
|
|
+ // Should not crash with null callback
|
|
|
+ delay(200);
|
|
|
+ ts.execute();
|
|
|
+ EXPECT_EQ(callback_counter, 0); // No callback executed
|
|
|
+ // Task should still run through its lifecycle
|
|
|
+}
|
|
|
+
|
|
|
+int main(int argc, char **argv) {
|
|
|
+ ::testing::InitGoogleTest(&argc, argv);
|
|
|
+ return RUN_ALL_TESTS();
|
|
|
+}
|