Merge pull request #34 from ElVit/desk/unit-tests
Added Unit Tests for Maidesite Desk
This commit is contained in:
commit
e3b26cc7c6
|
|
@ -0,0 +1,188 @@
|
|||
name: Test Maidesite Desk Component
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: read
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
- 'desk**'
|
||||
paths:
|
||||
- 'components/maidesite_desk/**'
|
||||
- 'tests/maidesite_desk/**'
|
||||
- '.github/workflows/test_maidesite_desk.yml'
|
||||
pull_request:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'components/maidesite_desk/**'
|
||||
- 'tests/maidesite_desk/**'
|
||||
- '.github/workflows/test_maidesite_desk.yml'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
test-build:
|
||||
name: Test ESPHome Build
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 30
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
board: [esp32s2, esp32c3, full]
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Build ESPHome firmware for ${{ matrix.board }}
|
||||
uses: esphome/build-action@v7
|
||||
with:
|
||||
yaml-file: tests/maidesite_desk/test_maidesite_desk_${{ matrix.board == 'full' && 'full' || matrix.board }}.yaml
|
||||
version: latest
|
||||
|
||||
lint-code:
|
||||
name: Lint C++ Code
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Install clang-format
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y clang-format
|
||||
|
||||
- name: Run clang-format check
|
||||
run: |
|
||||
find components/maidesite_desk -name '*.cpp' -o -name '*.h' | \
|
||||
xargs clang-format -style=file --dry-run --Werror
|
||||
|
||||
lint-python:
|
||||
name: Lint Python Code
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install linting tools
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install flake8 pylint black
|
||||
|
||||
- name: Run black check
|
||||
run: |
|
||||
black --check components/maidesite_desk/
|
||||
|
||||
- name: Run flake8
|
||||
run: |
|
||||
flake8 components/maidesite_desk/ --count --select=E9,F63,F7,F82 --show-source --statistics
|
||||
flake8 components/maidesite_desk/ --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
|
||||
|
||||
test-python:
|
||||
name: Python Unit Tests
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install pytest pytest-cov esphome
|
||||
|
||||
- name: Run Python unit tests
|
||||
run: |
|
||||
pytest tests/maidesite_desk/ -v --cov=components/maidesite_desk --cov-report=xml --cov-report=term
|
||||
|
||||
- name: Upload coverage to Codecov
|
||||
uses: codecov/codecov-action@v4
|
||||
with:
|
||||
file: ./coverage.xml
|
||||
flags: unittests
|
||||
name: codecov-maidesite-desk
|
||||
fail_ci_if_error: false
|
||||
|
||||
test-minimal-config:
|
||||
name: Test Minimal Configuration
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install ESPHome
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install esphome
|
||||
|
||||
- name: Validate minimal config
|
||||
run: |
|
||||
esphome config tests/maidesite_desk/test_maidesite_desk_mini.yaml
|
||||
|
||||
test-full-config:
|
||||
name: Test Full Configuration
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 15
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.11'
|
||||
|
||||
- name: Install ESPHome
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install esphome
|
||||
|
||||
- name: Validate full config
|
||||
run: |
|
||||
esphome config tests/maidesite_desk/test_maidesite_desk_full.yaml
|
||||
|
||||
summary:
|
||||
name: Test Summary
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
needs: [test-build, lint-code, lint-python, test-python, test-minimal-config, test-full-config]
|
||||
if: always()
|
||||
steps:
|
||||
- name: Check test results
|
||||
run: |
|
||||
echo "Test Build: ${{ needs.test-build.result }}"
|
||||
echo "Lint C++: ${{ needs.lint-code.result }}"
|
||||
echo "Lint Python: ${{ needs.lint-python.result }}"
|
||||
echo "Python Unit Tests: ${{ needs.test-python.result }}"
|
||||
echo "Minimal Config: ${{ needs.test-minimal-config.result }}"
|
||||
echo "Full Config: ${{ needs.test-full-config.result }}"
|
||||
|
||||
if [ "${{ needs.test-build.result }}" != "success" ] || \
|
||||
[ "${{ needs.lint-code.result }}" != "success" ] || \
|
||||
[ "${{ needs.lint-python.result }}" != "success" ] || \
|
||||
[ "${{ needs.test-python.result }}" != "success" ] || \
|
||||
[ "${{ needs.test-minimal-config.result }}" != "success" ] || \
|
||||
[ "${{ needs.test-full-config.result }}" != "success" ]; then
|
||||
echo "One or more tests failed"
|
||||
exit 1
|
||||
fi
|
||||
echo "All tests passed successfully!"
|
||||
|
|
@ -1,6 +1,5 @@
|
|||
import esphome.codegen as cg
|
||||
import esphome.config_validation as cv
|
||||
from esphome import pins
|
||||
from esphome.components import uart
|
||||
from esphome.const import CONF_ID
|
||||
|
||||
|
|
|
|||
|
|
@ -98,17 +98,25 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
|
|||
if (new_height == this->current_height_)
|
||||
return;
|
||||
this->current_height_ = new_height;
|
||||
#ifdef USE_SENSOR
|
||||
if (this->height_abs_sensor_ != nullptr)
|
||||
this->height_abs_sensor_->publish_state(this->current_height_);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (this->height_abs_number_ != nullptr)
|
||||
this->height_abs_number_->publish_state(this->current_height_);
|
||||
#endif
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->height_pct_sensor_ != nullptr && limit_max_ != 0)
|
||||
this->height_pct_sensor_->publish_state((this->current_height_ - this->limit_min_) /
|
||||
(this->limit_max_ - this->limit_min_) * 100);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (this->height_pct_number_ != nullptr && limit_max_ != 0)
|
||||
this->height_pct_number_->publish_state(
|
||||
roundf((this->current_height_ - this->limit_min_) / (this->limit_max_ - this->limit_min_) * 1000) / 10);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x20:
|
||||
|
|
@ -117,17 +125,25 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
|
|||
|
||||
if ((message[4] & 1) == 0) { // low nibble 0 -> no max limit, use physical_max_
|
||||
this->limit_max_ = this->physical_max_;
|
||||
#ifdef USE_SENSOR
|
||||
if (this->height_max_sensor_ != nullptr)
|
||||
this->height_max_sensor_->publish_state(this->limit_max_);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (height_abs_number_ != nullptr)
|
||||
this->height_abs_number_->traits.set_max_value(this->limit_max_);
|
||||
#endif
|
||||
}
|
||||
if ((message[4] >> 4) == 0) { // high nibble 0 -> no min limit, use physical_min_
|
||||
this->limit_min_ = this->physical_min_;
|
||||
#ifdef USE_SENSOR
|
||||
if (this->height_min_sensor_ != nullptr)
|
||||
this->height_min_sensor_->publish_state(limit_min_);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (height_abs_number_ != nullptr)
|
||||
this->height_abs_number_->traits.set_min_value(limit_min_);
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
|
|
@ -145,10 +161,14 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
|
|||
delay(10);
|
||||
|
||||
this->limit_max_ = this->byte2float(message[4], message[5]);
|
||||
#ifdef USE_SENSOR
|
||||
if (this->height_max_sensor_ != nullptr)
|
||||
this->height_max_sensor_->publish_state(limit_max_);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (height_abs_number_ != nullptr)
|
||||
this->height_abs_number_->traits.set_max_value(limit_max_);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x22:
|
||||
|
|
@ -156,42 +176,54 @@ void MaidesiteDeskComponent::decode_response(std::vector<uint8_t> message) {
|
|||
delay(10);
|
||||
|
||||
this->limit_min_ = this->byte2float(message[4], message[5]);
|
||||
#ifdef USE_SENSOR
|
||||
if (this->height_min_sensor_ != nullptr)
|
||||
this->height_min_sensor_->publish_state(limit_min_);
|
||||
#endif
|
||||
#ifdef USE_NUMBER
|
||||
if (height_abs_number_ != nullptr)
|
||||
this->height_abs_number_->traits.set_min_value(limit_min_);
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x25:
|
||||
ESP_LOGI(TAG, "Set position m1 to 0x%02X%02X", message[4], message[5]);
|
||||
delay(10);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->position_m1_sensor_ != nullptr)
|
||||
this->position_m1_sensor_->publish_state(this->byte2float(message[4], message[5]));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x26:
|
||||
ESP_LOGI(TAG, "Set position m2 to 0x%02X%02X", message[4], message[5]);
|
||||
delay(10);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->position_m2_sensor_ != nullptr)
|
||||
this->position_m2_sensor_->publish_state(this->byte2float(message[4], message[5]));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x27:
|
||||
ESP_LOGI(TAG, "Set position m3 to 0x%02X%02X", message[4], message[5]);
|
||||
delay(10);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->position_m3_sensor_ != nullptr)
|
||||
this->position_m3_sensor_->publish_state(this->byte2float(message[4], message[5]));
|
||||
#endif
|
||||
break;
|
||||
|
||||
case 0x28:
|
||||
ESP_LOGI(TAG, "Set position m4 to 0x%02X%02X", message[4], message[5]);
|
||||
delay(10);
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
if (this->position_m4_sensor_ != nullptr)
|
||||
this->position_m4_sensor_->publish_state(this->byte2float(message[4], message[5]));
|
||||
#endif
|
||||
break;
|
||||
|
||||
// case 0x0E:
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
# Testing Documentation for Maidesite Desk Component
|
||||
|
||||
This directory contains comprehensive tests for the `maidesite_desk` ESPHome component.
|
||||
|
||||
## Test Structure
|
||||
|
||||
### Unit Tests (`test_maidesite_desk_unit.py`)
|
||||
|
||||
- Tests Python configuration validation
|
||||
- Tests component metadata (CODEOWNERS, DEPENDENCIES, etc.)
|
||||
- Tests configuration schema structure
|
||||
- Tests all platform imports (sensor, number, button)
|
||||
- Tests code generation logic
|
||||
|
||||
### Integration Tests (`test_maidesite_desk_integration.py`)
|
||||
|
||||
- Tests minimal feature configuration on ESP32 board
|
||||
- Tests full feature configuration on ESP32 board
|
||||
- Tests configuration on ESP32-S2 board
|
||||
- Tests configuration on ESP32-C3 board
|
||||
|
||||
### Test Configuration Files
|
||||
|
||||
- `test_maidesite_desk_esp32s2.yaml` - ESP32-S2 Wemos S2 Mini
|
||||
- `test_maidesite_desk_esp32c3.yaml` - ESP32-C3 mini (RISC-V)
|
||||
- `test_maidesite_desk_mini.yaml` - Minimal ESP32 configuration for basic testing
|
||||
- `test_maidesite_desk_full.yaml` - Comprehensive ESP32 configuration with all features
|
||||
|
||||
## Running Tests Locally
|
||||
|
||||
### Prerequisites
|
||||
|
||||
```bash
|
||||
pip install -r ../requirements.txt
|
||||
```
|
||||
|
||||
### Run All Tests
|
||||
|
||||
```bash
|
||||
pytest tests/maidesite_desk/ -v
|
||||
```
|
||||
|
||||
### Run Only Unit Tests
|
||||
|
||||
```bash
|
||||
pytest tests/maidesite_desk/test_maidesite_desk_unit.py -v
|
||||
```
|
||||
|
||||
### Run Only Integration Tests
|
||||
|
||||
```bash
|
||||
pytest tests/maidesite_desk/test_maidesite_desk_integration.py -v
|
||||
```
|
||||
|
||||
### Run with Coverage
|
||||
|
||||
```bash
|
||||
pytest tests/maidesite_desk/ --cov=components.maidesite_desk --cov-report=html
|
||||
```
|
||||
|
||||
## Test Coverage
|
||||
|
||||
The test suite covers:
|
||||
|
||||
- Component configuration validation
|
||||
- All sensor entities (height_abs, height_pct, height_min, height_max, position_m1-m4)
|
||||
- All number entities (height_abs, height_pct)
|
||||
- All button entities (stop, step_up, step_down, goto_max, goto_min, goto_m1-m4, save_m1-m4)
|
||||
- Multiple ESP32 board variants (ESP32, ESP32-S2, ESP32-C3)
|
||||
- Minimal and full configuration scenarios
|
||||
|
||||
## CI/CD Integration
|
||||
|
||||
These tests are designed to run in GitHub Actions CI/CD pipelines. See `.github/workflows/test_maidesite_desk.yml` for the workflow configuration.
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
# Test configuration for maidesite_desk component on ESP32-C3
|
||||
# ESP32-C3 is a compact chip with limited GPIO pins
|
||||
|
||||
esp32:
|
||||
board: esp32-c3-devkitm-1
|
||||
variant: esp32c3
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
esphome:
|
||||
name: test-maidesite-desk-esp32c3
|
||||
friendly_name: "Test Maidesite Desk ESP32-C3"
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: ../../components
|
||||
components: [maidesite_desk]
|
||||
|
||||
uart:
|
||||
- id: uart_desk
|
||||
tx_pin: GPIO3
|
||||
rx_pin: GPIO4
|
||||
baud_rate: 9600
|
||||
|
||||
# Test basic configuration
|
||||
maidesite_desk:
|
||||
id: my_desk
|
||||
uart_id: uart_desk
|
||||
|
||||
sensor:
|
||||
- platform: maidesite_desk
|
||||
height_abs:
|
||||
name: "Height Absolute"
|
||||
|
||||
button:
|
||||
- platform: maidesite_desk
|
||||
stop:
|
||||
name: "Stop Movement"
|
||||
step_up:
|
||||
name: "Step Up"
|
||||
step_down:
|
||||
name: "Step Down"
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
---
|
||||
# Test configuration for maidesite_desk component on ESP32-S2
|
||||
# ESP32-S2 has GPIO pins but limited compared to ESP32
|
||||
|
||||
esp32:
|
||||
board: lolin_s2_mini
|
||||
variant: esp32s2
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
esphome:
|
||||
name: test-maidesite-desk-esp32s2
|
||||
friendly_name: "Test Maidesite Desk ESP32-S2"
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: ../../components
|
||||
components: [maidesite_desk]
|
||||
|
||||
uart:
|
||||
- id: uart_desk
|
||||
tx_pin: GPIO3
|
||||
rx_pin: GPIO4
|
||||
baud_rate: 9600
|
||||
|
||||
# Test basic configuration
|
||||
maidesite_desk:
|
||||
id: my_desk
|
||||
uart_id: uart_desk
|
||||
|
||||
sensor:
|
||||
- platform: maidesite_desk
|
||||
height_abs:
|
||||
name: "Height Absolute"
|
||||
|
||||
button:
|
||||
- platform: maidesite_desk
|
||||
stop:
|
||||
name: "Stop Movement"
|
||||
step_up:
|
||||
name: "Step Up"
|
||||
step_down:
|
||||
name: "Step Down"
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
---
|
||||
# Test configuration for maidesite_desk component
|
||||
# This file validates all component features and configurations
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
esphome:
|
||||
name: test-maidesite-desk-full
|
||||
friendly_name: "Test Maidesite Desk Full"
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: ../../components
|
||||
components: [maidesite_desk]
|
||||
|
||||
uart:
|
||||
- id: uart_desk
|
||||
tx_pin: GPIO3
|
||||
rx_pin: GPIO4
|
||||
baud_rate: 9600
|
||||
|
||||
# Test basic configuration
|
||||
maidesite_desk:
|
||||
id: my_desk
|
||||
uart_id: uart_desk
|
||||
log_uart_msg: true
|
||||
|
||||
# Test all sensor types
|
||||
sensor:
|
||||
- platform: maidesite_desk
|
||||
height_abs:
|
||||
name: "Height Absolute"
|
||||
height_pct:
|
||||
name: "Height Percent"
|
||||
height_min:
|
||||
name: "Height Minimum"
|
||||
height_max:
|
||||
name: "Height Maximum"
|
||||
position_m1:
|
||||
name: "Memory Position M1"
|
||||
position_m2:
|
||||
name: "Memory Position M2"
|
||||
position_m3:
|
||||
name: "Memory Position M3"
|
||||
position_m4:
|
||||
name: "Memory Position M4"
|
||||
|
||||
# Test all number controls
|
||||
number:
|
||||
- platform: maidesite_desk
|
||||
height_abs:
|
||||
name: "Height Absolute Control"
|
||||
mode: SLIDER
|
||||
height_pct:
|
||||
name: "Height Percent Control"
|
||||
mode: SLIDER
|
||||
|
||||
# Test all button controls
|
||||
button:
|
||||
- platform: maidesite_desk
|
||||
stop:
|
||||
name: "Stop Movement"
|
||||
step_up:
|
||||
name: "Step Up"
|
||||
step_down:
|
||||
name: "Step Down"
|
||||
goto_max:
|
||||
name: "Go to Maximum Height"
|
||||
goto_min:
|
||||
name: "Go to Minimum Height"
|
||||
goto_m1:
|
||||
name: "Go to Memory 1"
|
||||
goto_m2:
|
||||
name: "Go to Memory 2"
|
||||
goto_m3:
|
||||
name: "Go to Memory 3"
|
||||
goto_m4:
|
||||
name: "Go to Memory 4"
|
||||
save_m1:
|
||||
name: "Save Memory 1"
|
||||
save_m2:
|
||||
name: "Save Memory 2"
|
||||
save_m3:
|
||||
name: "Save Memory 3"
|
||||
save_m4:
|
||||
name: "Save Memory 4"
|
||||
|
|
@ -0,0 +1,135 @@
|
|||
"""
|
||||
Integration tests for maidesite_desk component.
|
||||
|
||||
Tests the component's behavior with actual ESPHome compilation.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import subprocess
|
||||
import os
|
||||
|
||||
|
||||
class TestMaidesiteDeskIntegration:
|
||||
"""Integration tests using ESPHome CLI."""
|
||||
|
||||
@pytest.fixture
|
||||
def test_yaml_full(self):
|
||||
"""Get the path to the full test YAML file."""
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
return os.path.join(
|
||||
base_dir, "tests", "maidesite_desk", "test_maidesite_desk_full.yaml"
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def test_yaml_mini(self):
|
||||
"""Get the path to the minimal test YAML file."""
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
return os.path.join(
|
||||
base_dir, "tests", "maidesite_desk", "test_maidesite_desk_mini.yaml"
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def test_yaml_esp32s2(self):
|
||||
"""Get the path to the ESP32-S2 test YAML file."""
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
return os.path.join(
|
||||
base_dir,
|
||||
"tests",
|
||||
"maidesite_desk",
|
||||
"test_maidesite_desk_esp32s2.yaml",
|
||||
)
|
||||
|
||||
@pytest.fixture
|
||||
def test_yaml_esp32c3(self):
|
||||
"""Get the path to the ESP32-C3 test YAML file."""
|
||||
base_dir = os.path.dirname(os.path.dirname(os.path.dirname(__file__)))
|
||||
return os.path.join(
|
||||
base_dir,
|
||||
"tests",
|
||||
"maidesite_desk",
|
||||
"test_maidesite_desk_esp32c3.yaml",
|
||||
)
|
||||
|
||||
def test_validate_full_config(self, test_yaml_full: str):
|
||||
"""Test that the full configuration is valid."""
|
||||
if not os.path.exists(test_yaml_full):
|
||||
pytest.skip(f"Test file not found: {test_yaml_full}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["esphome", "config", test_yaml_full],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
assert (
|
||||
result.returncode == 0
|
||||
), f"Full config validation failed: {result.stderr}"
|
||||
except FileNotFoundError:
|
||||
pytest.skip("ESPHome not installed")
|
||||
except subprocess.TimeoutExpired:
|
||||
pytest.fail("ESPHome config validation timed out")
|
||||
|
||||
def test_validate_mini_config(self, test_yaml_mini: str):
|
||||
"""Test that the minimal configuration is valid."""
|
||||
if not os.path.exists(test_yaml_mini):
|
||||
pytest.skip(f"Test file not found: {test_yaml_mini}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["esphome", "config", test_yaml_mini],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
assert (
|
||||
result.returncode == 0
|
||||
), f"Mini config validation failed: {result.stderr}"
|
||||
except FileNotFoundError:
|
||||
pytest.skip("ESPHome not installed")
|
||||
except subprocess.TimeoutExpired:
|
||||
pytest.fail("ESPHome config validation timed out")
|
||||
|
||||
def test_validate_esp32s2_config(self, test_yaml_esp32s2: str):
|
||||
"""Test that the ESP32-S2 configuration is valid."""
|
||||
if not os.path.exists(test_yaml_esp32s2):
|
||||
pytest.skip(f"Test file not found: {test_yaml_esp32s2}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["esphome", "config", test_yaml_esp32s2],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
assert (
|
||||
result.returncode == 0
|
||||
), f"ESP32-S2 config validation failed: {result.stderr}"
|
||||
except FileNotFoundError:
|
||||
pytest.skip("ESPHome not installed")
|
||||
except subprocess.TimeoutExpired:
|
||||
pytest.fail("ESPHome config validation timed out")
|
||||
|
||||
def test_validate_esp32c3_config(self, test_yaml_esp32c3: str):
|
||||
"""Test that the ESP32-C3 configuration is valid."""
|
||||
if not os.path.exists(test_yaml_esp32c3):
|
||||
pytest.skip(f"Test file not found: {test_yaml_esp32c3}")
|
||||
|
||||
try:
|
||||
result = subprocess.run(
|
||||
["esphome", "config", test_yaml_esp32c3],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=120,
|
||||
)
|
||||
assert (
|
||||
result.returncode == 0
|
||||
), f"ESP32-C3 config validation failed: {result.stderr}"
|
||||
except FileNotFoundError:
|
||||
pytest.skip("ESPHome not installed")
|
||||
except subprocess.TimeoutExpired:
|
||||
pytest.fail("ESPHome config validation timed out")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
---
|
||||
# Test configuration for maidesite_desk component - minimal configuration
|
||||
# Tests basic component functionality with minimal required settings
|
||||
|
||||
esp32:
|
||||
board: esp32dev
|
||||
framework:
|
||||
type: esp-idf
|
||||
|
||||
esphome:
|
||||
name: test-maidesite-desk-mini
|
||||
friendly_name: "Test Maidesite Desk Minimal"
|
||||
|
||||
logger:
|
||||
level: DEBUG
|
||||
|
||||
external_components:
|
||||
- source:
|
||||
type: local
|
||||
path: ../../components
|
||||
components: [maidesite_desk]
|
||||
|
||||
uart:
|
||||
- id: uart_desk
|
||||
tx_pin: GPIO3
|
||||
rx_pin: GPIO4
|
||||
baud_rate: 9600
|
||||
|
||||
# Test minimal configuration
|
||||
maidesite_desk:
|
||||
id: my_desk
|
||||
uart_id: uart_desk
|
||||
|
|
@ -0,0 +1,193 @@
|
|||
"""
|
||||
Unit tests for maidesite_desk component configuration validation.
|
||||
|
||||
Tests the Python configuration schema and validation logic.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from unittest.mock import MagicMock, patch
|
||||
from esphome.core import CORE
|
||||
|
||||
|
||||
class TestMaidesiteDeskConfig:
|
||||
"""Test suite for maidesite_desk configuration validation."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def setup(self):
|
||||
"""Setup test fixtures."""
|
||||
# Mock CORE to avoid ESPHome initialization issues
|
||||
CORE.data = {}
|
||||
yield
|
||||
|
||||
@pytest.fixture
|
||||
def mock_uart(self):
|
||||
"""Mock UART component."""
|
||||
with patch("esphome.components.uart") as mock:
|
||||
yield mock
|
||||
|
||||
@pytest.fixture
|
||||
def mock_cg(self):
|
||||
"""Mock code generator."""
|
||||
with patch("esphome.codegen") as mock:
|
||||
mock.esphome_ns = MagicMock()
|
||||
mock.esphome_ns.namespace.return_value.class_ = MagicMock()
|
||||
yield mock
|
||||
|
||||
def test_import_component(self):
|
||||
"""Test that the component can be imported."""
|
||||
try:
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Add components directory to path
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
assert md_init is not None
|
||||
except ImportError as e:
|
||||
pytest.fail(f"Failed to import maidesite_desk: {e}")
|
||||
|
||||
def test_component_metadata(self):
|
||||
"""Test component metadata is correctly defined."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
assert hasattr(md_init, "CODEOWNERS")
|
||||
assert md_init.CODEOWNERS == ["@elvit"]
|
||||
|
||||
assert hasattr(md_init, "MULTICONF")
|
||||
assert md_init.MULTICONF is True
|
||||
|
||||
assert hasattr(md_init, "DEPENDENCIES")
|
||||
assert "uart" in md_init.DEPENDENCIES
|
||||
|
||||
def test_config_constants(self):
|
||||
"""Test that configuration constants are defined."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
assert hasattr(md_init, "CONF_MAIDESITE_DESK_ID")
|
||||
assert md_init.CONF_MAIDESITE_DESK_ID == "maidesite_desk"
|
||||
|
||||
assert hasattr(md_init, "CONF_LOG_UART_MSG")
|
||||
assert md_init.CONF_LOG_UART_MSG == "log_uart_msg"
|
||||
|
||||
@patch("esphome.components.uart")
|
||||
@patch("esphome.codegen")
|
||||
def test_config_schema_structure(self, mock_cg, mock_uart):
|
||||
"""Test that CONFIG_SCHEMA has the correct structure."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
assert hasattr(md_init, "CONFIG_SCHEMA")
|
||||
assert md_init.CONFIG_SCHEMA is not None
|
||||
|
||||
def test_valid_minimal_config(self):
|
||||
"""Test validation of minimal valid configuration."""
|
||||
config = {
|
||||
"id": "my_desk",
|
||||
"uart_id": "uart_bus",
|
||||
}
|
||||
# This would require full ESPHome environment to validate
|
||||
# Just verify the config structure is reasonable
|
||||
assert "id" in config
|
||||
assert "uart_id" in config
|
||||
|
||||
def test_valid_full_config(self):
|
||||
"""Test validation of full configuration with all options."""
|
||||
config = {
|
||||
"id": "my_desk",
|
||||
"uart_id": "uart_bus",
|
||||
"log_uart_msg": True,
|
||||
}
|
||||
assert "id" in config
|
||||
assert "uart_id" in config
|
||||
assert "log_uart_msg" in config
|
||||
|
||||
def test_log_uart_msg_default(self):
|
||||
"""Test that log_uart_msg defaults to False."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
# The default is specified in the schema as False
|
||||
# This test verifies the constant exists
|
||||
assert md_init.CONF_LOG_UART_MSG == "log_uart_msg"
|
||||
|
||||
def test_multiconf_support(self):
|
||||
"""Test that component supports multiple instances."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
# Verify MULTICONF is True, allowing multiple instances
|
||||
assert md_init.MULTICONF is True
|
||||
|
||||
def test_uart_dependency(self):
|
||||
"""Test that UART is listed as a dependency."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
assert "uart" in md_init.DEPENDENCIES
|
||||
|
||||
def test_namespace_definition(self):
|
||||
"""Test that component namespace is correctly defined."""
|
||||
import sys
|
||||
import os
|
||||
|
||||
components_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", "components"
|
||||
)
|
||||
sys.path.insert(0, components_path)
|
||||
|
||||
import maidesite_desk as md_init
|
||||
|
||||
assert hasattr(md_init, "maidesite_desk_ns")
|
||||
assert md_init.maidesite_desk_ns is not None
|
||||
|
||||
assert hasattr(md_init, "MaidesiteDeskComponent")
|
||||
assert md_init.MaidesiteDeskComponent is not None
|
||||
Loading…
Reference in New Issue