1
0
mirror of https://github.com/esphome/esphome.git synced 2025-10-29 22:24:26 +00:00

Merge branch 'dev' into wifi_multi_sta_tests

This commit is contained in:
J. Nick Koston
2025-10-21 12:25:57 -10:00
committed by GitHub
36 changed files with 1013 additions and 91 deletions

5
tests/components/.gitignore vendored Normal file
View File

@@ -0,0 +1,5 @@
# Gitignore settings for ESPHome
# This is an example and may include too much for your use-case.
# You can modify this file to suit your needs.
/.esphome/
/secrets.yaml

View File

@@ -0,0 +1,32 @@
# How to write C++ ESPHome unit tests
1. Locate the folder with your component or create a new one with the same name as the component.
2. Write the tests. You can add as many `.cpp` and `.h` files as you need to organize your tests.
**IMPORTANT**: wrap all your testing code in a unique namespace to avoid linker collisions when compiling
testing binaries that combine many components. By convention, this unique namespace is `esphome::component::testing`
(where "component" is the component under test), for example: `esphome::uart::testing`.
## Running component unit tests
(from the repository root)
```bash
./script/cpp_unit_test.py component1 component2 ...
```
The above will compile and run the provided components and their tests.
To run all tests, you can invoke `cpp_unit_test.py` with the special `--all` flag:
```bash
./script/cpp_unit_test.py --all
```
To run a specific test suite, you can provide a Google Test filter:
```bash
GTEST_FILTER='UART*' ./script/cpp_unit_test.py uart modbus
```
The process will return `0` for success or nonzero for failure. In case of failure, the errors will be printed out to the console.

View File

@@ -70,3 +70,69 @@ binary_sensor:
- delay: 10s
time_off: 200ms
time_on: 800ms
# Test on_multi_click with single click
- platform: template
id: multi_click_single
name: "Multi Click Single"
on_multi_click:
- timing:
- state: true
min_length: 50ms
max_length: 350ms
then:
- logger.log: "Single click detected"
# Test on_multi_click with double click
- platform: template
id: multi_click_double
name: "Multi Click Double"
on_multi_click:
- timing:
- state: true
min_length: 50ms
max_length: 350ms
- state: false
min_length: 50ms
max_length: 350ms
- state: true
min_length: 50ms
max_length: 350ms
then:
- logger.log: "Double click detected"
# Test on_multi_click with complex pattern (5 events)
- platform: template
id: multi_click_complex
name: "Multi Click Complex"
on_multi_click:
- timing:
- state: true
min_length: 50ms
max_length: 350ms
- state: false
min_length: 50ms
max_length: 350ms
- state: true
min_length: 50ms
max_length: 350ms
- state: false
min_length: 50ms
max_length: 350ms
- state: true
min_length: 50ms
then:
- logger.log: "Complex pattern detected"
# Test on_multi_click with custom invalid_cooldown
- platform: template
id: multi_click_cooldown
name: "Multi Click Cooldown"
on_multi_click:
- timing:
- state: true
min_length: 100ms
max_length: 500ms
invalid_cooldown: 2s
then:
- logger.log: "Click with custom cooldown"

View File

@@ -12,3 +12,20 @@ switch:
- platform: gpio
pin: ${switch_pin}
id: gpio_switch
- platform: gpio
pin: ${switch_pin_2}
id: gpio_switch_interlock_1
interlock: [gpio_switch_interlock_2, gpio_switch_interlock_3]
interlock_wait_time: 100ms
- platform: gpio
pin: ${switch_pin_3}
id: gpio_switch_interlock_2
interlock: [gpio_switch_interlock_1, gpio_switch_interlock_3]
- platform: gpio
pin: ${switch_pin_4}
id: gpio_switch_interlock_3
interlock: [gpio_switch_interlock_1, gpio_switch_interlock_2]
interlock_wait_time: 50ms

View File

@@ -2,5 +2,8 @@ substitutions:
binary_sensor_pin: GPIO2
output_pin: GPIO3
switch_pin: GPIO4
switch_pin_2: GPIO5
switch_pin_3: GPIO6
switch_pin_4: GPIO7
<<: !include common.yaml

View File

@@ -2,5 +2,8 @@ substitutions:
binary_sensor_pin: GPIO12
output_pin: GPIO13
switch_pin: GPIO14
switch_pin_2: GPIO15
switch_pin_3: GPIO16
switch_pin_4: GPIO17
<<: !include common.yaml

View File

@@ -2,5 +2,8 @@ substitutions:
binary_sensor_pin: GPIO0
output_pin: GPIO2
switch_pin: GPIO15
switch_pin_2: GPIO12
switch_pin_3: GPIO13
switch_pin_4: GPIO14
<<: !include common.yaml

View File

@@ -12,3 +12,20 @@ switch:
- platform: gpio
pin: P1.2
id: gpio_switch
- platform: gpio
pin: P1.3
id: gpio_switch_interlock_1
interlock: [gpio_switch_interlock_2, gpio_switch_interlock_3]
interlock_wait_time: 100ms
- platform: gpio
pin: P1.4
id: gpio_switch_interlock_2
interlock: [gpio_switch_interlock_1, gpio_switch_interlock_3]
- platform: gpio
pin: P1.5
id: gpio_switch_interlock_3
interlock: [gpio_switch_interlock_1, gpio_switch_interlock_2]
interlock_wait_time: 50ms

View File

@@ -12,3 +12,20 @@ switch:
- platform: gpio
pin: P1.2
id: gpio_switch
- platform: gpio
pin: P1.3
id: gpio_switch_interlock_1
interlock: [gpio_switch_interlock_2, gpio_switch_interlock_3]
interlock_wait_time: 100ms
- platform: gpio
pin: P1.4
id: gpio_switch_interlock_2
interlock: [gpio_switch_interlock_1, gpio_switch_interlock_3]
- platform: gpio
pin: P1.5
id: gpio_switch_interlock_3
interlock: [gpio_switch_interlock_1, gpio_switch_interlock_2]
interlock_wait_time: 50ms

View File

@@ -2,5 +2,8 @@ substitutions:
binary_sensor_pin: GPIO2
output_pin: GPIO3
switch_pin: GPIO4
switch_pin_2: GPIO5
switch_pin_3: GPIO6
switch_pin_4: GPIO7
<<: !include common.yaml

View File

@@ -123,3 +123,43 @@ light:
red: 100%
green: 50%
blue: 50%
# Test StrobeLightEffect with multiple colors
- platform: monochromatic
id: test_strobe_multiple
name: Strobe Multiple Colors
output: test_ledc_1
effects:
- strobe:
name: Strobe Multi
colors:
- state: true
brightness: 100%
duration: 500ms
- state: false
duration: 250ms
- state: true
brightness: 50%
duration: 500ms
# Test StrobeLightEffect with transition
- platform: rgb
id: test_strobe_transition
name: Strobe With Transition
red: test_ledc_1
green: test_ledc_2
blue: test_ledc_3
effects:
- strobe:
name: Strobe Transition
colors:
- state: true
red: 100%
green: 0%
blue: 0%
duration: 1s
transition_length: 500ms
- state: true
red: 0%
green: 100%
blue: 0%
duration: 1s
transition_length: 500ms

26
tests/components/main.cpp Normal file
View File

@@ -0,0 +1,26 @@
#include <gtest/gtest.h>
/*
This special main.cpp replaces the default one.
It will run all the Google Tests found in all compiled cpp files and then exit with the result
See README.md for more information
*/
// Auto generated code by esphome
// ========== AUTO GENERATED INCLUDE BLOCK BEGIN ===========
// ========== AUTO GENERATED INCLUDE BLOCK END ==========="
void original_setup() {
// This function won't be run.
// ========== AUTO GENERATED CODE BEGIN ===========
// =========== AUTO GENERATED CODE END ============
}
void setup() {
::testing::InitGoogleTest();
int exit_code = RUN_ALL_TESTS();
exit(exit_code);
}
void loop() {}

View File

@@ -0,0 +1,37 @@
#pragma once
#include <vector>
#include <cstdint>
#include <cstring>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include "esphome/components/uart/uart_component.h"
namespace esphome::uart::testing {
using ::testing::_;
using ::testing::Return;
using ::testing::SaveArg;
using ::testing::DoAll;
using ::testing::Invoke;
using ::testing::SetArgPointee;
// Derive a mock from UARTComponent to test the wrapper implementations.
class MockUARTComponent : public UARTComponent {
public:
using UARTComponent::write_array;
using UARTComponent::write_byte;
// NOTE: std::vector is used here for test convenience. For production code,
// consider using StaticVector or FixedVector from esphome/core/helpers.h instead.
std::vector<uint8_t> written_data;
void write_array(const uint8_t *data, size_t len) override { written_data.assign(data, data + len); }
MOCK_METHOD(bool, read_array, (uint8_t * data, size_t len), (override));
MOCK_METHOD(bool, peek_byte, (uint8_t * data), (override));
MOCK_METHOD(int, available, (), (override));
MOCK_METHOD(void, flush, (), (override));
MOCK_METHOD(void, check_logger_conflict, (), (override));
};
} // namespace esphome::uart::testing

View File

@@ -0,0 +1,73 @@
#include "common.h"
namespace esphome::uart::testing {
TEST(UARTComponentTest, SetGetBaudRate) {
MockUARTComponent mock;
mock.set_baud_rate(38400);
EXPECT_EQ(mock.get_baud_rate(), 38400);
}
TEST(UARTComponentTest, SetGetStopBits) {
MockUARTComponent mock;
mock.set_stop_bits(2);
EXPECT_EQ(mock.get_stop_bits(), 2);
}
TEST(UARTComponentTest, SetGetDataBits) {
MockUARTComponent mock;
mock.set_data_bits(7);
EXPECT_EQ(mock.get_data_bits(), 7);
}
TEST(UARTComponentTest, SetGetParity) {
MockUARTComponent mock;
mock.set_parity(UARTParityOptions::UART_CONFIG_PARITY_EVEN);
EXPECT_EQ(mock.get_parity(), UARTParityOptions::UART_CONFIG_PARITY_EVEN);
}
TEST(UARTComponentTest, SetGetRxBufferSize) {
MockUARTComponent mock;
mock.set_rx_buffer_size(128);
EXPECT_EQ(mock.get_rx_buffer_size(), 128);
}
TEST(UARTComponentTest, WriteArrayVector) {
MockUARTComponent mock;
std::vector<uint8_t> data = {10, 20, 30};
mock.write_array(data);
EXPECT_EQ(mock.written_data, data);
}
TEST(UARTComponentTest, WriteByte) {
MockUARTComponent mock;
uint8_t byte = 0x79;
mock.write_byte(byte);
EXPECT_EQ(mock.written_data.size(), 1);
EXPECT_EQ(mock.written_data[0], byte);
}
TEST(UARTComponentTest, WriteStr) {
MockUARTComponent mock;
const char *str = "Hello";
std::vector<uint8_t> captured;
mock.write_str(str);
EXPECT_EQ(mock.written_data.size(), strlen(str));
EXPECT_EQ(0, strncmp(str, (const char *) mock.written_data.data(), mock.written_data.size()));
}
// Tests for wrapper methods forwarding to pure virtual read_array
TEST(UARTComponentTest, ReadByteSuccess) {
MockUARTComponent mock;
uint8_t value = 0;
EXPECT_CALL(mock, read_array(&value, 1)).WillOnce(Return(true));
EXPECT_TRUE(mock.read_byte(&value));
}
TEST(UARTComponentTest, ReadByteFailure) {
MockUARTComponent mock;
uint8_t value = 0xFF;
EXPECT_CALL(mock, read_array(&value, 1)).WillOnce(Return(false));
EXPECT_FALSE(mock.read_byte(&value));
}
} // namespace esphome::uart::testing

View File

@@ -0,0 +1,108 @@
#include "common.h"
#include "esphome/components/uart/uart.h"
namespace esphome::uart::testing {
TEST(UARTDeviceTest, ReadByteSuccess) {
MockUARTComponent mock;
UARTDevice dev(&mock);
uint8_t value = 0;
EXPECT_CALL(mock, read_array(_, 1)).WillOnce(DoAll(SetArgPointee<0>(0x5A), Return(true)));
bool result = dev.read_byte(&value);
EXPECT_TRUE(result);
EXPECT_EQ(value, 0x5A);
}
TEST(UARTDeviceTest, ReadByteFailure) {
MockUARTComponent mock;
UARTDevice dev(&mock);
uint8_t value = 0xFF;
EXPECT_CALL(mock, read_array(_, 1)).WillOnce(Return(false));
bool result = dev.read_byte(&value);
EXPECT_FALSE(result);
}
TEST(UARTDeviceTest, PeekByteSuccess) {
MockUARTComponent mock;
UARTDevice dev(&mock);
uint8_t value = 0;
EXPECT_CALL(mock, peek_byte(_)).WillOnce(DoAll(SetArgPointee<0>(0xA5), Return(true)));
bool result = dev.peek_byte(&value);
EXPECT_TRUE(result);
EXPECT_EQ(value, 0xA5);
}
TEST(UARTDeviceTest, PeekByteFailure) {
MockUARTComponent mock;
UARTDevice dev(&mock);
uint8_t value = 0;
EXPECT_CALL(mock, peek_byte(_)).WillOnce(Return(false));
bool result = dev.peek_byte(&value);
EXPECT_FALSE(result);
}
TEST(UARTDeviceTest, Available) {
MockUARTComponent mock;
UARTDevice dev(&mock);
EXPECT_CALL(mock, available()).WillOnce(Return(5));
EXPECT_EQ(dev.available(), 5);
}
TEST(UARTDeviceTest, FlushCallsParent) {
MockUARTComponent mock;
UARTDevice dev(&mock);
EXPECT_CALL(mock, flush()).Times(1);
dev.flush();
}
TEST(UARTDeviceTest, WriteByteForwardsToWriteArray) {
MockUARTComponent mock;
UARTDevice dev(&mock);
dev.write_byte(0xAB);
EXPECT_EQ(mock.written_data.size(), 1);
EXPECT_EQ(mock.written_data[0], 0xAB);
}
TEST(UARTDeviceTest, WriteArrayPointer) {
MockUARTComponent mock;
UARTDevice dev(&mock);
uint8_t data[3] = {1, 2, 3};
dev.write_array(data, 3);
EXPECT_EQ(mock.written_data.size(), 3);
EXPECT_EQ(mock.written_data, std::vector(data, data + 3));
}
TEST(UARTDeviceTest, WriteArrayVector) {
MockUARTComponent mock;
UARTDevice dev(&mock);
std::vector<uint8_t> data = {4, 5, 6};
dev.write_array(data);
EXPECT_EQ(mock.written_data, data);
}
TEST(UARTDeviceTest, WriteArrayStdArray) {
MockUARTComponent mock;
UARTDevice dev(&mock);
std::array<uint8_t, 4> data = {7, 8, 9, 10};
dev.write_array(data);
EXPECT_EQ(mock.written_data.size(), data.size());
EXPECT_EQ(mock.written_data, std::vector(data.begin(), data.end()));
}
TEST(UARTDeviceTest, WriteStrForwardsToWriteArray) {
MockUARTComponent mock;
UARTDevice dev(&mock);
const char *str = "ESPHome";
dev.write_str(str);
EXPECT_EQ(mock.written_data.size(), strlen(str));
EXPECT_EQ(0, strncmp(str, (const char *) mock.written_data.data(), mock.written_data.size()));
}
TEST(UARTDeviceTest, WriteStrEmptyString) {
MockUARTComponent mock;
UARTDevice dev(&mock);
const char *str = "";
dev.write_str(str);
EXPECT_EQ(mock.written_data.size(), 0);
}
} // namespace esphome::uart::testing