diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 0000000..3dfc63e --- /dev/null +++ b/TESTING.md @@ -0,0 +1,350 @@ +# Testing Guide for ESP32 Dimmer Driver + +## Overview + +This guide provides instructions for running and extending the unit tests for the ESP32 Dimmer Driver library. + +## Test Coverage Summary + +The unit test suite covers 16 test cases across the following categories: + +### 1. Dimmer Creation (2 tests) +- Creating a single dimmer instance +- Creating multiple dimmer instances with different GPIO pins + +### 2. Power Control (3 tests) +- Setting and getting power with normal values (0, 50, 99) +- Boundary value testing (values >99 are clamped) +- Power retrieval when dimmer is OFF + +### 3. State Management (3 tests) +- Setting and getting ON/OFF state +- Toggling state with changeState() +- State's effect on getPower() return value + +### 4. Mode Management (3 tests) +- Setting and getting NORMAL/TOGGLE modes +- Toggle settings with valid ranges +- Toggle settings with boundary values + +### 5. Initialization (2 tests) +- Initialization with NORMAL_MODE +- Initialization with TOGGLE_MODE + +### 6. Multiple Dimmers (1 test) +- Independent operation of multiple dimmers + +### 7. Integration (2 tests) +- State changes affecting power reporting +- Complete workflow validation + +## Prerequisites + +### Software Requirements +- ESP-IDF v5.0 or higher +- Python 3.7 or higher (for ESP-IDF) +- CMake 3.16 or higher +- Appropriate compiler toolchain for your platform + +### Hardware Requirements +- ESP32 development board (any variant: ESP32, ESP32-S2, ESP32-C3, etc.) +- USB cable for flashing and monitoring +- (Optional) Oscilloscope for hardware validation + +## Installation + +1. Install ESP-IDF v5.0 or higher following the [official guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/get-started/index.html) + +2. Clone this repository: + ```bash + git clone https://github.com/pmarchini/Esp32Dimmer.git + cd Esp32Dimmer + ``` + +## Running Tests + +### Quick Start + +```bash +# Navigate to test application +cd test_app + +# Set target (choose your ESP32 variant) +idf.py set-target esp32 + +# Build the tests +idf.py build + +# Flash and monitor (replace with your serial port) +idf.py -p /dev/ttyUSB0 flash monitor +``` + +### Supported Targets + +The tests can run on any ESP32 variant: +- ESP32 (default) +- ESP32-S2 +- ESP32-C3 +- ESP32-S3 + +To set a specific target: +```bash +idf.py set-target esp32s3 +``` + +### Build Options + +For verbose build output: +```bash +idf.py -v build +``` + +For clean build: +```bash +idf.py fullclean +idf.py build +``` + +## Understanding Test Output + +### Successful Test Run + +``` +======================================== +ESP32 Dimmer Driver Unit Tests +======================================== + +Running test_createDimmer_returns_valid_pointer... +PASS + +Running test_createDimmer_with_different_pins... +PASS + +[... more tests ...] + +======================================== +All tests completed! +======================================== + +16 Tests 0 Failures 0 Ignored +OK +``` + +### Failed Test Example + +If a test fails, you'll see: +``` +Running test_setPower_getPower_normal_values... +test_main.c:56:test_setPower_getPower_normal_values:FAIL: Expected 50 Was 0 + +16 Tests 1 Failures 0 Ignored +FAIL +``` + +## Writing New Tests + +### Test Function Template + +```c +void test_your_feature_description(void) +{ + // 1. Setup - Create dimmer and initialize + dimmertyp *dimmer = createDimmer(TEST_TRIAC_GPIO, TEST_ZC_GPIO); + TEST_ASSERT_NOT_NULL(dimmer); + begin(dimmer, NORMAL_MODE, ON, 50); + + // 2. Execute - Perform the operation you're testing + setPower(dimmer, 42); + vTaskDelay(pdMS_TO_TICKS(10)); + + // 3. Verify - Check the results + TEST_ASSERT_EQUAL(42, getPower(dimmer)); + + // 4. Cleanup (if needed) + // Note: Library doesn't provide cleanup functions +} +``` + +### Unity Assertion Macros + +Common assertions used in the tests: + +```c +TEST_ASSERT_TRUE(condition) // Verify condition is true +TEST_ASSERT_FALSE(condition) // Verify condition is false +TEST_ASSERT_EQUAL(expected, actual) // Verify values are equal +TEST_ASSERT_NOT_EQUAL(val1, val2) // Verify values are different +TEST_ASSERT_NULL(pointer) // Verify pointer is NULL +TEST_ASSERT_NOT_NULL(pointer) // Verify pointer is not NULL +TEST_ASSERT_EQUAL_INT(exp, act) // Compare integers +TEST_ASSERT_GREATER_THAN(threshold, actual) // Verify actual > threshold +TEST_ASSERT_LESS_THAN(threshold, actual) // Verify actual < threshold +``` + +### Adding a New Test + +1. Add test function to `test_app/main/test_main.c`: + ```c + void test_my_new_feature(void) + { + // Your test code here + } + ``` + +2. Register it in `app_main()`: + ```c + void app_main(void) + { + UNITY_BEGIN(); + + // Existing tests... + RUN_TEST(test_my_new_feature); // Add this line + + UNITY_END(); + } + ``` + +3. Rebuild and test: + ```bash + idf.py build flash monitor + ``` + +## Test Best Practices + +### 1. Test Independence +Each test should be independent and not rely on state from other tests. + +### 2. Descriptive Names +Use clear, descriptive test names: `test__` +- Good: `test_setPower_boundary_values` +- Bad: `test_power_1` + +### 3. Test One Thing +Each test should verify one specific behavior. + +### 4. Use Delays +When testing asynchronous operations, add small delays: +```c +setPower(dimmer, 50); +vTaskDelay(pdMS_TO_TICKS(10)); // Allow setting to take effect +TEST_ASSERT_EQUAL(50, getPower(dimmer)); +``` + +### 5. Document Edge Cases +Add comments for non-obvious test cases: +```c +// Test that power values above 99 are clamped to 99 +setPower(dimmer, 150); +TEST_ASSERT_EQUAL(99, getPower(dimmer)); +``` + +## Continuous Integration + +### GitHub Actions Example + +Create `.github/workflows/test.yml`: + +```yaml +name: ESP32 Dimmer Tests + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - name: Setup ESP-IDF + uses: espressif/esp-idf-ci-action@v1 + with: + esp_idf_version: v5.0 + target: esp32 + - name: Build Tests + run: | + cd test_app + idf.py build +``` + +## Troubleshooting + +### Build Errors + +**Error: "No such file or directory" for esp32-triac-dimmer-driver.h** +- Solution: Ensure EXTRA_COMPONENT_DIRS is set correctly in test_app/CMakeLists.txt + +**Error: "undefined reference to unity_*"** +- Solution: Ensure unity is in the REQUIRES list in main/CMakeLists.txt + +### Runtime Issues + +**Tests fail to start** +- Check serial port permissions: `sudo usermod -a -G dialout $USER` +- Verify correct serial port: `ls /dev/tty*` + +**Random test failures** +- Hardware interference - ensure stable power supply +- Increase delay times in tests +- Check for race conditions in test logic + +### Memory Issues + +**Stack overflow in tests** +- Increase CONFIG_ESP_MAIN_TASK_STACK_SIZE in sdkconfig.defaults + +**Heap allocation failures** +- Tests may exceed ALL_DIMMERS limit (50) +- Reset the board between test runs + +## Hardware Validation Testing + +While unit tests validate software behavior, hardware testing validates actual dimmer operation: + +### Setup for Hardware Tests +1. Connect zero-crossing detector to GPIO_NUM_21 +2. Connect TRIAC outputs to GPIO_NUM_22 and GPIO_NUM_23 +3. Connect dimmable AC load (e.g., incandescent bulb) +4. Use oscilloscope to monitor TRIAC firing pulses + +### Safety Warnings +⚠️ **AC VOLTAGE HAZARD** ⚠️ +- Only qualified personnel should work with AC circuits +- Ensure proper isolation and grounding +- Use current-limited test circuits +- Never touch circuits while powered + +## Future Enhancements + +Potential areas for test expansion: + +1. **Performance Tests** + - Measure ISR timing + - Verify zero-crossing detection accuracy + - Test rapid power changes + +2. **Stress Tests** + - Maximum dimmer count (50 dimmers) + - Continuous operation for extended periods + - Rapid state/mode changes + +3. **Hardware-in-the-Loop Tests** + - Actual zero-crossing signal simulation + - TRIAC timing verification + - Multi-frequency testing (50Hz/60Hz) + +4. **Error Handling Tests** + - Invalid GPIO pins + - Null pointer handling + - Resource exhaustion scenarios + +## References + +- [ESP-IDF Unit Testing Guide](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-guides/unit-tests.html) +- [Unity Testing Framework](https://github.com/ThrowTheSwitch/Unity) +- [ESP32 GPIO Documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/peripherals/gpio.html) + +## Support + +For questions or issues: +- Open an issue on [GitHub](https://github.com/pmarchini/Esp32Dimmer/issues) +- Check existing issues and discussions +- Review the test code for examples diff --git a/test_app/README.md b/test_app/README.md index 3c434a8..1da109e 100644 --- a/test_app/README.md +++ b/test_app/README.md @@ -41,6 +41,20 @@ The tests are organized using the ESP-IDF Unity testing framework and cover the - ESP-IDF v5.0 or higher installed - ESP32 development board (tests can run without actual hardware connected) +### Validate Your Setup + +Before running tests, you can validate your environment: + +```bash +./validate_setup.sh +``` + +This will check: +- Python and ESP-IDF installation +- Required files and directories +- Serial port availability +- CMake installation + ### Build and Flash 1. Navigate to the test_app directory: diff --git a/test_app/sdkconfig.defaults b/test_app/sdkconfig.defaults new file mode 100644 index 0000000..0d062de --- /dev/null +++ b/test_app/sdkconfig.defaults @@ -0,0 +1,14 @@ +# ESP32 Dimmer Test Configuration + +# Enable Unity test framework +CONFIG_UNITY_ENABLE_FLOAT=y +CONFIG_UNITY_ENABLE_DOUBLE=y + +# Increase main task stack size for tests +CONFIG_ESP_MAIN_TASK_STACK_SIZE=8192 + +# Enable detailed logging for tests +CONFIG_LOG_DEFAULT_LEVEL_INFO=y + +# FreeRTOS configuration +CONFIG_FREERTOS_HZ=1000 diff --git a/test_app/validate_setup.sh b/test_app/validate_setup.sh new file mode 100755 index 0000000..a20a6a7 --- /dev/null +++ b/test_app/validate_setup.sh @@ -0,0 +1,174 @@ +#!/bin/bash + +# ESP32 Dimmer Test Setup Validation Script +# This script checks if the environment is properly configured for running tests + +set -e + +echo "=========================================" +echo "ESP32 Dimmer Test Setup Validator" +echo "=========================================" +echo "" + +# Color codes +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +errors=0 +warnings=0 + +# Function to print status +check_pass() { + echo -e "${GREEN}✓${NC} $1" +} + +check_fail() { + echo -e "${RED}✗${NC} $1" + ((errors++)) +} + +check_warn() { + echo -e "${YELLOW}⚠${NC} $1" + ((warnings++)) +} + +echo "Checking prerequisites..." +echo "" + +# Check Python +if command -v python3 &> /dev/null; then + PYTHON_VERSION=$(python3 --version | cut -d' ' -f2) + check_pass "Python 3 found: $PYTHON_VERSION" +else + check_fail "Python 3 not found" +fi + +# Check IDF_PATH +if [ -n "$IDF_PATH" ]; then + check_pass "IDF_PATH is set: $IDF_PATH" + + # Check if IDF_PATH exists + if [ -d "$IDF_PATH" ]; then + check_pass "ESP-IDF directory exists" + + # Check for idf.py + if [ -f "$IDF_PATH/tools/idf.py" ]; then + check_pass "idf.py found" + else + check_fail "idf.py not found in IDF_PATH" + fi + else + check_fail "IDF_PATH directory does not exist" + fi +else + check_fail "IDF_PATH not set" + echo " Run: . \$HOME/esp/esp-idf/export.sh" +fi + +# Check for idf.py in PATH +if command -v idf.py &> /dev/null; then + check_pass "idf.py is in PATH" +else + check_warn "idf.py not in PATH (you may need to run export.sh)" +fi + +# Check CMake +if command -v cmake &> /dev/null; then + CMAKE_VERSION=$(cmake --version | head -n1 | cut -d' ' -f3) + check_pass "CMake found: $CMAKE_VERSION" +else + check_fail "CMake not found" +fi + +# Check for test_app directory +if [ -d "test_app" ]; then + check_pass "test_app directory found" + + # Check for required files + if [ -f "test_app/CMakeLists.txt" ]; then + check_pass "test_app/CMakeLists.txt found" + else + check_fail "test_app/CMakeLists.txt not found" + fi + + if [ -f "test_app/main/test_main.c" ]; then + check_pass "test_app/main/test_main.c found" + else + check_fail "test_app/main/test_main.c not found" + fi +else + check_fail "test_app directory not found (are you in the repository root?)" +fi + +# Check for component directory +if [ -d "src/components/esp32-triac-dimmer-driver" ]; then + check_pass "Component directory found" + + if [ -f "src/components/esp32-triac-dimmer-driver/esp32-triac-dimmer-driver.c" ]; then + check_pass "Component source file found" + else + check_fail "Component source file not found" + fi +else + check_fail "Component directory not found" +fi + +# Check serial ports +echo "" +echo "Available serial ports:" +if ls /dev/ttyUSB* 1> /dev/null 2>&1; then + for port in /dev/ttyUSB*; do + if [ -r "$port" ] && [ -w "$port" ]; then + check_pass "$(basename $port) (read/write access)" + else + check_warn "$(basename $port) (no read/write access - check permissions)" + fi + done +elif ls /dev/ttyACM* 1> /dev/null 2>&1; then + for port in /dev/ttyACM*; do + if [ -r "$port" ] && [ -w "$port" ]; then + check_pass "$(basename $port) (read/write access)" + else + check_warn "$(basename $port) (no read/write access - check permissions)" + fi + done +elif ls /dev/cu.* 1> /dev/null 2>&1; then + for port in /dev/cu.usbserial-*; do + if [ -e "$port" ]; then + check_pass "$(basename $port)" + fi + done +else + check_warn "No serial ports found (ESP32 may not be connected)" +fi + +# Summary +echo "" +echo "=========================================" +echo "Summary" +echo "=========================================" +echo "Errors: $errors" +echo "Warnings: $warnings" +echo "" + +if [ $errors -eq 0 ]; then + if [ $warnings -eq 0 ]; then + echo -e "${GREEN}✓ All checks passed!${NC}" + echo "" + echo "You can now run tests:" + echo " cd test_app" + echo " idf.py set-target esp32" + echo " idf.py build" + echo " idf.py -p /dev/ttyUSB0 flash monitor" + else + echo -e "${YELLOW}⚠ Setup is OK but has warnings${NC}" + echo "Check warnings above and resolve if needed" + fi + exit 0 +else + echo -e "${RED}✗ Setup has errors${NC}" + echo "Please resolve the errors above before running tests" + exit 1 +fi