Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
_codeql_detected_source_root
23 changes: 23 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,29 @@ To use the basic example, add the component to your project's components directo

If you are using the library in a project that is not using ESP-IDF 5.x, you can still use the old version of the library (v1.0.0) which is compatible with ESP-IDF 4.x.

## Testing

This library includes comprehensive unit tests to ensure reliability and enable safe refactoring. The tests cover all public API functions including:

- Dimmer creation and initialization
- Power control with boundary conditions
- State management (ON/OFF)
- Mode management (NORMAL/TOGGLE)
- Multiple independent dimmers

### Running Tests

To run the unit tests:

```bash
cd test_app
idf.py set-target esp32
idf.py build
idf.py -p /dev/ttyUSB0 flash monitor
```

See [test_app/README.md](test_app/README.md) for detailed testing documentation.

## Contributing

We welcome contributions to this library. Please open a pull request or an issue to get started.
Expand Down
356 changes: 356 additions & 0 deletions TESTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
# 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 19 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. GPIO and Timer ISR (5 tests)
- GPIO output timing after zero-crossing events
- Pulse width timing on GPIO output
- Dimmer response to zero-crossing interrupts
- Multiple dimmers controlling GPIO pins independently
- Timer ISR respecting dimmer state changes (ON/OFF)

### 8. Integration (1 test)
- State changes affecting power reporting

## 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_<component>_<scenario>`
- 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
8 changes: 8 additions & 0 deletions test_app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
build/
sdkconfig
sdkconfig.old
*.swp
*.swo
*.pyc
dependencies.lock
managed_components/
Loading