mirror of
https://github.com/esphome/esphome.git
synced 2026-09-28 15:30:21 +00:00
[cpptests] support testing platform components (#13075)
Co-authored-by: J. Nick Koston <nick@home-assistant.io> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
J. Nick Koston
J. Nick Koston
parent
00f809f5f0
commit
e82f0f4432
@@ -7,10 +7,23 @@
|
||||
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`.
|
||||
|
||||
### Platform components
|
||||
|
||||
For components that expose to a platform component, create a folder under your component test folder with the platform component name, e.g. `binary_sensor` and
|
||||
include the relevant `.cpp` and `.h` test files there.
|
||||
|
||||
### Override component code generation for testing
|
||||
|
||||
When generating code for testing, ESPHome won't invoke the component's `to_code` function, since most components do not
|
||||
need to generate configuration code for testing.
|
||||
|
||||
If you do need to generate code to for example configure compilation flags or add libraries,
|
||||
add the component name to the `CPP_TESTING_CODEGEN_COMPONENTS` allowlist in `script/cpp_unit_test.py`.
|
||||
|
||||
## Running component unit tests
|
||||
|
||||
(from the repository root)
|
||||
|
||||
```bash
|
||||
./script/cpp_unit_test.py component1 component2 ...
|
||||
```
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
#include "../common.h"
|
||||
|
||||
namespace esphome::packet_transport::testing {
|
||||
|
||||
TEST(PacketTransportBinarySensorTest, AddBinarySensor) {
|
||||
TestablePacketTransport transport;
|
||||
binary_sensor::BinarySensor bs;
|
||||
transport.add_binary_sensor("motion", &bs);
|
||||
ASSERT_EQ(transport.binary_sensors_.size(), 1u);
|
||||
EXPECT_STREQ(transport.binary_sensors_[0].id, "motion");
|
||||
EXPECT_EQ(transport.binary_sensors_[0].sensor, &bs);
|
||||
}
|
||||
|
||||
TEST(PacketTransportBinarySensorTest, AddRemoteBinarySensor) {
|
||||
TestablePacketTransport transport;
|
||||
binary_sensor::BinarySensor bs;
|
||||
transport.add_remote_binary_sensor("host1", "remote_motion", &bs);
|
||||
EXPECT_TRUE(transport.providers_.contains("host1"));
|
||||
EXPECT_EQ(transport.remote_binary_sensors_["host1"]["remote_motion"], &bs);
|
||||
}
|
||||
|
||||
TEST(PacketTransportBinarySensorTest, UnencryptedBinarySensorRoundTrip) {
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
binary_sensor::BinarySensor local_bs;
|
||||
local_bs.state = true;
|
||||
encoder.add_binary_sensor("motion", &local_bs);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
binary_sensor::BinarySensor remote_bs;
|
||||
decoder.add_remote_binary_sensor("sender", "motion", &remote_bs);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
EXPECT_TRUE(remote_bs.state);
|
||||
}
|
||||
|
||||
TEST(PacketTransportBinarySensorTest, MultipleSensorsRoundTrip) {
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
|
||||
sensor::Sensor s1, s2;
|
||||
s1.state = 10.0f;
|
||||
s2.state = 20.0f;
|
||||
encoder.add_sensor("s1", &s1);
|
||||
encoder.add_sensor("s2", &s2);
|
||||
|
||||
binary_sensor::BinarySensor bs1;
|
||||
bs1.state = true;
|
||||
encoder.add_binary_sensor("bs1", &bs1);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor rs1, rs2;
|
||||
binary_sensor::BinarySensor rbs1;
|
||||
rs1.state = -999.0f;
|
||||
rs2.state = -999.0f;
|
||||
decoder.add_remote_sensor("sender", "s1", &rs1);
|
||||
decoder.add_remote_sensor("sender", "s2", &rs2);
|
||||
decoder.add_remote_binary_sensor("sender", "bs1", &rbs1);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
|
||||
EXPECT_FLOAT_EQ(rs1.state, 10.0f);
|
||||
EXPECT_FLOAT_EQ(rs2.state, 20.0f);
|
||||
EXPECT_TRUE(rbs1.state);
|
||||
}
|
||||
|
||||
} // namespace esphome::packet_transport::testing
|
||||
@@ -1,11 +0,0 @@
|
||||
# Extra component configuration required by C++ unit tests.
|
||||
# Loaded by cpp_unit_test.py and merged into the test build config
|
||||
# before validation, so that platform defines (USE_SENSOR, etc.) are generated.
|
||||
|
||||
sensor:
|
||||
- platform: template
|
||||
id: test_cpp_sensor
|
||||
|
||||
binary_sensor:
|
||||
- platform: template
|
||||
id: test_cpp_binary_sensor
|
||||
@@ -65,198 +65,6 @@ TEST(PacketTransportTest, SetProviderEncryption) {
|
||||
EXPECT_EQ(transport.providers_["host1"].encryption_key, key);
|
||||
}
|
||||
|
||||
// --- Sensor management (requires USE_SENSOR / USE_BINARY_SENSOR) ---
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
TEST(PacketTransportTest, AddSensor) {
|
||||
TestablePacketTransport transport;
|
||||
sensor::Sensor s;
|
||||
transport.add_sensor("temp", &s);
|
||||
ASSERT_EQ(transport.sensors_.size(), 1u);
|
||||
EXPECT_STREQ(transport.sensors_[0].id, "temp");
|
||||
EXPECT_EQ(transport.sensors_[0].sensor, &s);
|
||||
EXPECT_TRUE(transport.sensors_[0].updated);
|
||||
}
|
||||
|
||||
TEST(PacketTransportTest, AddRemoteSensor) {
|
||||
TestablePacketTransport transport;
|
||||
sensor::Sensor s;
|
||||
transport.add_remote_sensor("host1", "remote_temp", &s);
|
||||
EXPECT_TRUE(transport.providers_.contains("host1"));
|
||||
EXPECT_EQ(transport.remote_sensors_["host1"]["remote_temp"], &s);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
TEST(PacketTransportTest, AddBinarySensor) {
|
||||
TestablePacketTransport transport;
|
||||
binary_sensor::BinarySensor bs;
|
||||
transport.add_binary_sensor("motion", &bs);
|
||||
ASSERT_EQ(transport.binary_sensors_.size(), 1u);
|
||||
EXPECT_STREQ(transport.binary_sensors_[0].id, "motion");
|
||||
EXPECT_EQ(transport.binary_sensors_[0].sensor, &bs);
|
||||
}
|
||||
|
||||
TEST(PacketTransportTest, AddRemoteBinarySensor) {
|
||||
TestablePacketTransport transport;
|
||||
binary_sensor::BinarySensor bs;
|
||||
transport.add_remote_binary_sensor("host1", "remote_motion", &bs);
|
||||
EXPECT_TRUE(transport.providers_.contains("host1"));
|
||||
EXPECT_EQ(transport.remote_binary_sensors_["host1"]["remote_motion"], &bs);
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Unencrypted round-trip tests (require USE_SENSOR / USE_BINARY_SENSOR) ---
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
TEST(PacketTransportTest, UnencryptedSensorRoundTrip) {
|
||||
// Encoder
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 42.5f;
|
||||
encoder.add_sensor("temp", &local_sensor);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
// Decoder
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f; // sentinel
|
||||
decoder.add_remote_sensor("sender", "temp", &remote_sensor);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, 42.5f);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef USE_BINARY_SENSOR
|
||||
TEST(PacketTransportTest, UnencryptedBinarySensorRoundTrip) {
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
binary_sensor::BinarySensor local_bs;
|
||||
local_bs.state = true;
|
||||
encoder.add_binary_sensor("motion", &local_bs);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
binary_sensor::BinarySensor remote_bs;
|
||||
decoder.add_remote_binary_sensor("sender", "motion", &remote_bs);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
EXPECT_TRUE(remote_bs.state);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if defined(USE_SENSOR) && defined(USE_BINARY_SENSOR)
|
||||
TEST(PacketTransportTest, MultipleSensorsRoundTrip) {
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
|
||||
sensor::Sensor s1, s2;
|
||||
s1.state = 10.0f;
|
||||
s2.state = 20.0f;
|
||||
encoder.add_sensor("s1", &s1);
|
||||
encoder.add_sensor("s2", &s2);
|
||||
|
||||
binary_sensor::BinarySensor bs1;
|
||||
bs1.state = true;
|
||||
encoder.add_binary_sensor("bs1", &bs1);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor rs1, rs2;
|
||||
binary_sensor::BinarySensor rbs1;
|
||||
rs1.state = -999.0f;
|
||||
rs2.state = -999.0f;
|
||||
decoder.add_remote_sensor("sender", "s1", &rs1);
|
||||
decoder.add_remote_sensor("sender", "s2", &rs2);
|
||||
decoder.add_remote_binary_sensor("sender", "bs1", &rbs1);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
|
||||
EXPECT_FLOAT_EQ(rs1.state, 10.0f);
|
||||
EXPECT_FLOAT_EQ(rs2.state, 20.0f);
|
||||
EXPECT_TRUE(rbs1.state);
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Encrypted round-trip ---
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
TEST(PacketTransportTest, EncryptedSensorRoundTrip) {
|
||||
std::vector<uint8_t> key(32);
|
||||
for (int i = 0; i < 32; i++)
|
||||
key[i] = i;
|
||||
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
encoder.set_encryption_key(key);
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 99.9f;
|
||||
encoder.add_sensor("temp", &local_sensor);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f;
|
||||
decoder.add_remote_sensor("sender", "temp", &remote_sensor);
|
||||
decoder.set_provider_encryption("sender", key);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, 99.9f);
|
||||
}
|
||||
|
||||
// --- Selective send ---
|
||||
|
||||
TEST(PacketTransportTest, SendDataOnlyUpdated) {
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
|
||||
sensor::Sensor s1, s2;
|
||||
s1.state = 1.0f;
|
||||
s2.state = 2.0f;
|
||||
encoder.add_sensor("s1", &s1);
|
||||
encoder.add_sensor("s2", &s2);
|
||||
|
||||
// Mark s1 as not updated, only s2 as updated
|
||||
encoder.sensors_[0].updated = false;
|
||||
encoder.sensors_[1].updated = true;
|
||||
|
||||
encoder.send_data_(false);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor rs1, rs2;
|
||||
rs1.state = -999.0f;
|
||||
rs2.state = -999.0f;
|
||||
decoder.add_remote_sensor("sender", "s1", &rs1);
|
||||
decoder.add_remote_sensor("sender", "s2", &rs2);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
|
||||
EXPECT_FLOAT_EQ(rs1.state, -999.0f); // not updated, not sent
|
||||
EXPECT_FLOAT_EQ(rs2.state, 2.0f); // updated, sent
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Ping key tests ---
|
||||
|
||||
TEST(PacketTransportTest, PingKeyStoredWhenEncrypted) {
|
||||
@@ -319,73 +127,6 @@ TEST(PacketTransportTest, PingKeyMaxLimit) {
|
||||
EXPECT_FALSE(transport.ping_keys_.contains("host4"));
|
||||
}
|
||||
|
||||
#ifdef USE_SENSOR
|
||||
TEST(PacketTransportTest, PingKeyIncludedInTransmittedPacket) {
|
||||
std::vector<uint8_t> key(32, 0xBB);
|
||||
|
||||
// Responder: encrypted, owns a sensor
|
||||
TestablePacketTransport responder;
|
||||
responder.init_for_test("responder");
|
||||
responder.set_encryption_key(key);
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 77.7f;
|
||||
responder.add_sensor("temp", &local_sensor);
|
||||
|
||||
// Requester sends a MAGIC_PING that the responder processes
|
||||
auto ping = build_ping_packet("requester", 0xDEADBEEF);
|
||||
responder.process_({ping.data(), ping.size()});
|
||||
ASSERT_EQ(responder.ping_keys_.size(), 1u);
|
||||
|
||||
// Responder sends sensor data — ping key should be embedded
|
||||
responder.send_data_(true);
|
||||
ASSERT_EQ(responder.sent_packets.size(), 1u);
|
||||
|
||||
// Requester: encrypted provider, ping-pong enabled, expects key 0xDEADBEEF
|
||||
TestablePacketTransport requester;
|
||||
requester.init_for_test("requester");
|
||||
requester.set_ping_pong_enable(true);
|
||||
requester.ping_key_ = 0xDEADBEEF;
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f;
|
||||
requester.add_remote_sensor("responder", "temp", &remote_sensor);
|
||||
requester.set_provider_encryption("responder", key);
|
||||
|
||||
// The requester decrypts the packet and finds its ping key echoed back,
|
||||
// which gates the sensor data — if the key is missing, data is blocked.
|
||||
auto &packet = responder.sent_packets[0];
|
||||
requester.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, 77.7f);
|
||||
}
|
||||
|
||||
TEST(PacketTransportTest, MissingPingKeyBlocksSensorData) {
|
||||
std::vector<uint8_t> key(32, 0xBB);
|
||||
|
||||
// Responder sends data WITHOUT receiving any MAGIC_PING first — no ping keys
|
||||
TestablePacketTransport responder;
|
||||
responder.init_for_test("responder");
|
||||
responder.set_encryption_key(key);
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 77.7f;
|
||||
responder.add_sensor("temp", &local_sensor);
|
||||
responder.send_data_(true);
|
||||
ASSERT_EQ(responder.sent_packets.size(), 1u);
|
||||
|
||||
// Requester with ping-pong enabled expects a key that isn't in the packet
|
||||
TestablePacketTransport requester;
|
||||
requester.init_for_test("requester");
|
||||
requester.set_ping_pong_enable(true);
|
||||
requester.ping_key_ = 0xDEADBEEF;
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f;
|
||||
requester.add_remote_sensor("responder", "temp", &remote_sensor);
|
||||
requester.set_provider_encryption("responder", key);
|
||||
|
||||
auto &packet = responder.sent_packets[0];
|
||||
requester.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, -999.0f); // blocked — ping key not found
|
||||
}
|
||||
#endif
|
||||
|
||||
// --- Process error handling ---
|
||||
|
||||
TEST(PacketTransportTest, ProcessShortBuffer) {
|
||||
|
||||
@@ -0,0 +1,170 @@
|
||||
#include "../common.h"
|
||||
|
||||
namespace esphome::packet_transport::testing {
|
||||
|
||||
TEST(PacketTransportSensorTest, AddSensor) {
|
||||
TestablePacketTransport transport;
|
||||
sensor::Sensor s;
|
||||
transport.add_sensor("temp", &s);
|
||||
ASSERT_EQ(transport.sensors_.size(), 1u);
|
||||
EXPECT_STREQ(transport.sensors_[0].id, "temp");
|
||||
EXPECT_EQ(transport.sensors_[0].sensor, &s);
|
||||
EXPECT_TRUE(transport.sensors_[0].updated);
|
||||
}
|
||||
|
||||
TEST(PacketTransportSensorTest, AddRemoteSensor) {
|
||||
TestablePacketTransport transport;
|
||||
sensor::Sensor s;
|
||||
transport.add_remote_sensor("host1", "remote_temp", &s);
|
||||
EXPECT_TRUE(transport.providers_.contains("host1"));
|
||||
EXPECT_EQ(transport.remote_sensors_["host1"]["remote_temp"], &s);
|
||||
}
|
||||
|
||||
TEST(PacketTransportSensorTest, UnencryptedSensorRoundTrip) {
|
||||
// Encoder
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 42.5f;
|
||||
encoder.add_sensor("temp", &local_sensor);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
// Decoder
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f; // sentinel
|
||||
decoder.add_remote_sensor("sender", "temp", &remote_sensor);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, 42.5f);
|
||||
}
|
||||
|
||||
TEST(PacketTransportSensorTest, EncryptedSensorRoundTrip) {
|
||||
std::vector<uint8_t> key(32);
|
||||
for (int i = 0; i < 32; i++)
|
||||
key[i] = i;
|
||||
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
encoder.set_encryption_key(key);
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 99.9f;
|
||||
encoder.add_sensor("temp", &local_sensor);
|
||||
|
||||
encoder.send_data_(true);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f;
|
||||
decoder.add_remote_sensor("sender", "temp", &remote_sensor);
|
||||
decoder.set_provider_encryption("sender", key);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, 99.9f);
|
||||
}
|
||||
|
||||
TEST(PacketTransportSensorTest, SendDataOnlyUpdated) {
|
||||
TestablePacketTransport encoder;
|
||||
encoder.init_for_test("sender");
|
||||
|
||||
sensor::Sensor s1, s2;
|
||||
s1.state = 1.0f;
|
||||
s2.state = 2.0f;
|
||||
encoder.add_sensor("s1", &s1);
|
||||
encoder.add_sensor("s2", &s2);
|
||||
|
||||
// Mark s1 as not updated, only s2 as updated
|
||||
encoder.sensors_[0].updated = false;
|
||||
encoder.sensors_[1].updated = true;
|
||||
|
||||
encoder.send_data_(false);
|
||||
ASSERT_EQ(encoder.sent_packets.size(), 1u);
|
||||
|
||||
TestablePacketTransport decoder;
|
||||
decoder.init_for_test("receiver");
|
||||
sensor::Sensor rs1, rs2;
|
||||
rs1.state = -999.0f;
|
||||
rs2.state = -999.0f;
|
||||
decoder.add_remote_sensor("sender", "s1", &rs1);
|
||||
decoder.add_remote_sensor("sender", "s2", &rs2);
|
||||
|
||||
auto &packet = encoder.sent_packets[0];
|
||||
decoder.process_({packet.data(), packet.size()});
|
||||
|
||||
EXPECT_FLOAT_EQ(rs1.state, -999.0f); // not updated, not sent
|
||||
EXPECT_FLOAT_EQ(rs2.state, 2.0f); // updated, sent
|
||||
}
|
||||
|
||||
TEST(PacketTransportSensorTest, PingKeyIncludedInTransmittedPacket) {
|
||||
std::vector<uint8_t> key(32, 0xBB);
|
||||
|
||||
// Responder: encrypted, owns a sensor
|
||||
TestablePacketTransport responder;
|
||||
responder.init_for_test("responder");
|
||||
responder.set_encryption_key(key);
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 77.7f;
|
||||
responder.add_sensor("temp", &local_sensor);
|
||||
|
||||
// Requester sends a MAGIC_PING that the responder processes
|
||||
auto ping = build_ping_packet("requester", 0xDEADBEEF);
|
||||
responder.process_({ping.data(), ping.size()});
|
||||
ASSERT_EQ(responder.ping_keys_.size(), 1u);
|
||||
|
||||
// Responder sends sensor data — ping key should be embedded
|
||||
responder.send_data_(true);
|
||||
ASSERT_EQ(responder.sent_packets.size(), 1u);
|
||||
|
||||
// Requester: encrypted provider, ping-pong enabled, expects key 0xDEADBEEF
|
||||
TestablePacketTransport requester;
|
||||
requester.init_for_test("requester");
|
||||
requester.set_ping_pong_enable(true);
|
||||
requester.ping_key_ = 0xDEADBEEF;
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f;
|
||||
requester.add_remote_sensor("responder", "temp", &remote_sensor);
|
||||
requester.set_provider_encryption("responder", key);
|
||||
|
||||
// The requester decrypts the packet and finds its ping key echoed back,
|
||||
// which gates the sensor data — if the key is missing, data is blocked.
|
||||
auto &packet = responder.sent_packets[0];
|
||||
requester.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, 77.7f);
|
||||
}
|
||||
|
||||
TEST(PacketTransportSensorTest, MissingPingKeyBlocksSensorData) {
|
||||
std::vector<uint8_t> key(32, 0xBB);
|
||||
|
||||
// Responder sends data WITHOUT receiving any MAGIC_PING first — no ping keys
|
||||
TestablePacketTransport responder;
|
||||
responder.init_for_test("responder");
|
||||
responder.set_encryption_key(key);
|
||||
sensor::Sensor local_sensor;
|
||||
local_sensor.state = 77.7f;
|
||||
responder.add_sensor("temp", &local_sensor);
|
||||
responder.send_data_(true);
|
||||
ASSERT_EQ(responder.sent_packets.size(), 1u);
|
||||
|
||||
// Requester with ping-pong enabled expects a key that isn't in the packet
|
||||
TestablePacketTransport requester;
|
||||
requester.init_for_test("requester");
|
||||
requester.set_ping_pong_enable(true);
|
||||
requester.ping_key_ = 0xDEADBEEF;
|
||||
sensor::Sensor remote_sensor;
|
||||
remote_sensor.state = -999.0f;
|
||||
requester.add_remote_sensor("responder", "temp", &remote_sensor);
|
||||
requester.set_provider_encryption("responder", key);
|
||||
|
||||
auto &packet = responder.sent_packets[0];
|
||||
requester.process_({packet.data(), packet.size()});
|
||||
EXPECT_FLOAT_EQ(remote_sensor.state, -999.0f); // blocked — ping key not found
|
||||
}
|
||||
|
||||
} // namespace esphome::packet_transport::testing
|
||||
@@ -1027,6 +1027,73 @@ def test_get_all_dependencies_empty_set() -> None:
|
||||
assert result == set()
|
||||
|
||||
|
||||
def test_get_all_dependencies_platform_component() -> None:
|
||||
"""Platform components (domain.component) are looked up via get_platform,
|
||||
not get_component."""
|
||||
platform_comp = Mock()
|
||||
platform_comp.dependencies = []
|
||||
platform_comp.auto_load = []
|
||||
|
||||
with (
|
||||
patch("esphome.loader.get_component") as mock_get_component,
|
||||
patch("helpers.get_platform") as mock_get_platform,
|
||||
):
|
||||
mock_get_platform.return_value = platform_comp
|
||||
mock_get_component.return_value = None
|
||||
|
||||
result = helpers.get_all_dependencies({"sensor.bthome"})
|
||||
|
||||
mock_get_platform.assert_called_once_with("sensor", "bthome")
|
||||
mock_get_component.assert_not_called()
|
||||
assert result == {"sensor.bthome"}
|
||||
|
||||
|
||||
def test_get_all_dependencies_platform_component_with_dependencies() -> None:
|
||||
"""Dependencies of a platform component are resolved transitively."""
|
||||
platform_comp = Mock()
|
||||
platform_comp.dependencies = ["sensor"]
|
||||
platform_comp.auto_load = []
|
||||
|
||||
sensor_comp = Mock()
|
||||
sensor_comp.dependencies = []
|
||||
sensor_comp.auto_load = []
|
||||
|
||||
with (
|
||||
patch("esphome.loader.get_component") as mock_get_component,
|
||||
patch("helpers.get_platform") as mock_get_platform,
|
||||
):
|
||||
mock_get_platform.return_value = platform_comp
|
||||
mock_get_component.side_effect = lambda name: (
|
||||
sensor_comp if name == "sensor" else None
|
||||
)
|
||||
|
||||
result = helpers.get_all_dependencies({"sensor.bthome"})
|
||||
|
||||
assert result == {"sensor.bthome", "sensor"}
|
||||
|
||||
|
||||
def test_get_all_dependencies_cpp_testing_flag() -> None:
|
||||
"""cpp_testing=True propagates to CORE.cpp_testing during resolution."""
|
||||
from esphome.core import CORE
|
||||
|
||||
with (
|
||||
patch("esphome.loader.get_component") as mock_get_component,
|
||||
patch("esphome.loader.get_platform"),
|
||||
):
|
||||
observed: list[bool] = []
|
||||
|
||||
def capturing_get_component(name: str):
|
||||
observed.append(CORE.cpp_testing)
|
||||
|
||||
mock_get_component.side_effect = capturing_get_component
|
||||
|
||||
helpers.get_all_dependencies({"some_comp"}, cpp_testing=True)
|
||||
|
||||
assert observed and all(observed), (
|
||||
"CORE.cpp_testing should be True during resolution"
|
||||
)
|
||||
|
||||
|
||||
def test_get_components_from_integration_fixtures() -> None:
|
||||
"""Test extraction of components from fixture YAML files."""
|
||||
yaml_content = {
|
||||
|
||||
@@ -841,6 +841,18 @@ class TestEsphomeCore:
|
||||
|
||||
assert "WiFi" in target.platformio_libraries
|
||||
|
||||
def test_testing_ensure_platform_registered__sets_count(self, target):
|
||||
"""Test testing_ensure_platform_registered sets count to 1 for new platform."""
|
||||
assert target.platform_counts["sensor"] == 0
|
||||
target.testing_ensure_platform_registered("sensor")
|
||||
assert target.platform_counts["sensor"] == 1
|
||||
|
||||
def test_testing_ensure_platform_registered__does_not_overwrite(self, target):
|
||||
"""Test testing_ensure_platform_registered preserves existing count."""
|
||||
target.platform_counts["sensor"] = 3
|
||||
target.testing_ensure_platform_registered("sensor")
|
||||
assert target.platform_counts["sensor"] == 3
|
||||
|
||||
def test_add_library__extracts_short_name_from_path(self, target):
|
||||
"""Test add_library extracts short name from library paths like owner/lib."""
|
||||
target.data[const.KEY_CORE] = {
|
||||
|
||||
Reference in New Issue
Block a user