[packet_interface] Fix clang-tidy findings in tests

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
clydebarrow
2026-10-20 08:41:48 +11:00
co-authored by Claude Opus 5.5
parent ecf4d24be6
commit d24ef14dc0
@@ -60,47 +60,49 @@ class TestTransport : public EntityTransport {
using EntityTransport::clear_remote_states_;
};
static Packet header(uint8_t type, uint32_t dictionary_id) {
return {type, static_cast<uint8_t>(dictionary_id), static_cast<uint8_t>(dictionary_id >> 8),
static_cast<uint8_t>(dictionary_id >> 16), static_cast<uint8_t>(dictionary_id >> 24)};
static Packet header(uint8_t packet_type, uint32_t id) {
return {packet_type, static_cast<uint8_t>(id), static_cast<uint8_t>(id >> 8), static_cast<uint8_t>(id >> 16),
static_cast<uint8_t>(id >> 24)};
}
class EntityTransportTest : public ::testing::Test {
protected:
void SetUp() override {
new (&App) Application;
App.pre_setup(DEVICE_NAME, strlen(DEVICE_NAME), "", 0);
static char name[] = "this-device";
static char friendly_name[] = "";
App.pre_setup(name, sizeof(name) - 1, friendly_name, 0);
}
void TearDown() override { App.~Application(); }
// The sender, with a sensor and a binary sensor
std::unique_ptr<TestTransport> make_sender() {
auto transport = std::make_unique<TestTransport>(&this->link_a);
std::unique_ptr<TestTransport> make_sender_() {
auto transport = std::make_unique<TestTransport>(&this->link_a_);
transport->set_entity_count(2);
transport->add_sensor("temp", &this->temp);
transport->add_binary_sensor("door", &this->door);
transport->add_sensor("temp", &this->temp_);
transport->add_binary_sensor("door", &this->door_);
transport->setup();
return transport;
}
// The receiver, showing the sender's entities; both run on this device, so they share its name
std::unique_ptr<TestTransport> make_receiver(const char *provider = DEVICE_NAME, uint32_t timeout_ms = 0) {
auto transport = std::make_unique<TestTransport>(&this->link_b);
std::unique_ptr<TestTransport> make_receiver_(const char *provider = DEVICE_NAME, uint32_t timeout_ms = 0) {
auto transport = std::make_unique<TestTransport>(&this->link_b_);
transport->set_remote_timeout(timeout_ms);
transport->add_remote_sensor(provider, "temp", &this->remote_temp);
transport->add_remote_binary_sensor(provider, "door", &this->remote_door);
transport->add_remote_sensor(provider, "temp", &this->remote_temp_);
transport->add_remote_binary_sensor(provider, "door", &this->remote_door_);
transport->setup();
return transport;
}
MockInterface link_a, link_b;
sensor::Sensor temp, remote_temp;
binary_sensor::BinarySensor door, remote_door;
MockInterface link_a_, link_b_;
sensor::Sensor temp_, remote_temp_;
binary_sensor::BinarySensor door_, remote_door_;
};
TEST_F(EntityTransportTest, SendsDictionaryThenStates) {
this->temp.publish_state(21.5f);
this->door.publish_state(true);
auto sender = this->make_sender();
this->temp_.publish_state(21.5f);
this->door_.publish_state(true);
auto sender = this->make_sender_();
sender->loop();
uint32_t id = sender->get_dictionary_id();
Packet dictionary = header(PACKET_DICTIONARY, id);
@@ -109,204 +111,204 @@ TEST_F(EntityTransportTest, SendsDictionaryThenStates) {
dictionary.insert(dictionary.end(), {1, 0, 1, 4, 't', 'e', 'm', 'p', 2, 0, 1, 4, 'd', 'o', 'o', 'r'});
Packet data = header(PACKET_DATA, id);
data.insert(data.end(), {1, 0, 0x00, 0x00, 0xAC, 0x41, 0, 1});
ASSERT_EQ(this->link_a.sent.size(), 2);
EXPECT_EQ(this->link_a.sent[0], dictionary);
EXPECT_EQ(this->link_a.sent[1], data);
ASSERT_EQ(this->link_a_.sent.size(), 2);
EXPECT_EQ(this->link_a_.sent[0], dictionary);
EXPECT_EQ(this->link_a_.sent[1], data);
}
TEST_F(EntityTransportTest, SendsOnlyChangedStates) {
auto sender = this->make_sender();
auto sender = this->make_sender_();
sender->loop();
this->link_a.sent.clear();
this->temp.publish_state(3.0f);
this->link_a_.sent.clear();
this->temp_.publish_state(3.0f);
sender->loop();
Packet data = header(PACKET_DATA, sender->get_dictionary_id());
data.insert(data.end(), {1, 0, 0x00, 0x00, 0x40, 0x40});
ASSERT_EQ(this->link_a.sent.size(), 1);
EXPECT_EQ(this->link_a.sent[0], data);
ASSERT_EQ(this->link_a_.sent.size(), 1);
EXPECT_EQ(this->link_a_.sent[0], data);
}
TEST_F(EntityTransportTest, StatesReachRemoteEntities) {
this->temp.publish_state(12.25f);
this->door.publish_state(true);
auto sender = this->make_sender();
auto receiver = this->make_receiver();
this->temp_.publish_state(12.25f);
this->door_.publish_state(true);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_();
sender->loop();
this->link_a.deliver_to(this->link_b);
EXPECT_EQ(this->remote_temp.get_state(), 12.25f);
EXPECT_TRUE(this->remote_door.state);
this->door.invalidate_state();
this->link_a_.deliver_to(this->link_b_);
EXPECT_EQ(this->remote_temp_.get_state(), 12.25f);
EXPECT_TRUE(this->remote_door_.state);
this->door_.invalidate_state();
sender->loop();
this->link_a.deliver_to(this->link_b);
EXPECT_FALSE(this->remote_door.has_state());
this->link_a_.deliver_to(this->link_b_);
EXPECT_FALSE(this->remote_door_.has_state());
}
TEST_F(EntityTransportTest, SplitsAcrossPackets) {
this->link_a.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 5);
this->temp.publish_state(1.0f);
this->door.publish_state(true);
auto sender = this->make_sender();
auto receiver = this->make_receiver();
this->link_a_.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 5);
this->temp_.publish_state(1.0f);
this->door_.publish_state(true);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_();
sender->loop();
EXPECT_EQ(this->link_a.sent.size(), 3); // one dictionary packet for each entity, then one data packet
this->link_a.deliver_to(this->link_b);
EXPECT_EQ(this->remote_temp.get_state(), 1.0f);
EXPECT_TRUE(this->remote_door.state);
EXPECT_EQ(this->link_a_.sent.size(), 3); // one dictionary packet for each entity, then one data packet
this->link_a_.deliver_to(this->link_b_);
EXPECT_EQ(this->remote_temp_.get_state(), 1.0f);
EXPECT_TRUE(this->remote_door_.state);
}
TEST_F(EntityTransportTest, RefusedPacketSentAgain) {
this->link_a.accept = false;
auto sender = this->make_sender();
this->link_a_.accept = false;
auto sender = this->make_sender_();
sender->loop();
EXPECT_TRUE(this->link_a.sent.empty());
this->link_a.accept = true;
EXPECT_TRUE(this->link_a_.sent.empty());
this->link_a_.accept = true;
sender->loop();
EXPECT_EQ(this->link_a.sent.size(), 2);
EXPECT_EQ(this->link_a_.sent.size(), 2);
}
TEST_F(EntityTransportTest, UnknownDictionaryRequested) {
this->temp.publish_state(5.0f);
auto sender = this->make_sender();
auto receiver = this->make_receiver();
this->temp_.publish_state(5.0f);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_();
sender->loop();
this->link_a.sent.erase(this->link_a.sent.begin()); // the dictionary is lost
this->link_a.deliver_to(this->link_b);
EXPECT_FALSE(this->remote_temp.has_state());
ASSERT_EQ(this->link_b.sent.size(), 1);
EXPECT_EQ(this->link_b.sent[0], header(PACKET_REQUEST, sender->get_dictionary_id()));
this->link_b.deliver_to(this->link_a);
this->link_a_.sent.erase(this->link_a_.sent.begin()); // the dictionary is lost
this->link_a_.deliver_to(this->link_b_);
EXPECT_FALSE(this->remote_temp_.has_state());
ASSERT_EQ(this->link_b_.sent.size(), 1);
EXPECT_EQ(this->link_b_.sent[0], header(PACKET_REQUEST, sender->get_dictionary_id()));
this->link_b_.deliver_to(this->link_a_);
sender->loop();
this->link_a.deliver_to(this->link_b);
EXPECT_EQ(this->remote_temp.get_state(), 5.0f);
this->link_a_.deliver_to(this->link_b_);
EXPECT_EQ(this->remote_temp_.get_state(), 5.0f);
}
TEST_F(EntityTransportTest, RequestsLimited) {
auto receiver = this->make_receiver();
auto receiver = this->make_receiver_();
Packet data = header(PACKET_DATA, 0x12345678);
data.push_back(0);
this->link_b.inject(data);
this->link_b.inject(data);
EXPECT_EQ(this->link_b.sent.size(), 1);
this->link_b_.inject(data);
this->link_b_.inject(data);
EXPECT_EQ(this->link_b_.sent.size(), 1);
}
TEST_F(EntityTransportTest, OtherProviderAndOwnPacketsIgnored) {
this->temp.publish_state(5.0f);
auto sender = this->make_sender();
auto receiver = this->make_receiver("other-device");
this->temp_.publish_state(5.0f);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_("other-device");
sender->loop();
auto packets = this->link_a.sent;
this->link_a.deliver_to(this->link_b);
EXPECT_FALSE(this->remote_temp.has_state());
auto packets = this->link_a_.sent;
this->link_a_.deliver_to(this->link_b_);
EXPECT_FALSE(this->remote_temp_.has_state());
for (auto &packet : packets)
this->link_a.inject(packet);
this->link_a.inject(header(PACKET_REQUEST, 0x12345678));
this->link_a_.inject(packet);
this->link_a_.inject(header(PACKET_REQUEST, 0x12345678));
sender->loop();
EXPECT_TRUE(this->link_a.sent.empty());
EXPECT_TRUE(this->link_a_.sent.empty());
}
TEST_F(EntityTransportTest, MalformedPacketsIgnored) {
auto sender = this->make_sender();
auto receiver = this->make_receiver();
auto sender = this->make_sender_();
auto receiver = this->make_receiver_();
sender->loop();
Packet dictionary = this->link_a.sent[0];
Packet data = this->link_a.sent[1];
this->link_b.inject(Packet(dictionary.begin(), dictionary.end() - 1));
this->link_b.inject(dictionary);
this->link_b.inject(Packet(data.begin(), data.end() - 1));
this->link_b.inject({PACKET_DATA, 1, 2});
this->link_b.inject({0x21, 0, 0, 0, 0});
EXPECT_FALSE(this->remote_temp.has_state());
Packet dictionary = this->link_a_.sent[0];
Packet data = this->link_a_.sent[1];
this->link_b_.inject(Packet(dictionary.begin(), dictionary.end() - 1));
this->link_b_.inject(dictionary);
this->link_b_.inject(Packet(data.begin(), data.end() - 1));
this->link_b_.inject({PACKET_DATA, 1, 2});
this->link_b_.inject({0x21, 0, 0, 0, 0});
EXPECT_FALSE(this->remote_temp_.has_state());
}
TEST_F(EntityTransportTest, UpdateIntervalFollowsCapabilities) {
EXPECT_EQ(this->make_sender()->get_update_interval(), 15000);
this->link_a.set_capabilities(packet_interface::CAPABILITY_SESSION);
EXPECT_EQ(this->make_sender()->get_update_interval(), 15000);
this->link_a.set_capabilities(packet_interface::CAPABILITY_SESSION | packet_interface::CAPABILITY_RELIABILITY);
EXPECT_EQ(this->make_sender()->get_update_interval(), SCHEDULER_DONT_RUN);
auto transport = std::make_unique<TestTransport>(&this->link_a);
EXPECT_EQ(this->make_sender_()->get_update_interval(), 15000);
this->link_a_.set_capabilities(packet_interface::CAPABILITY_SESSION);
EXPECT_EQ(this->make_sender_()->get_update_interval(), 15000);
this->link_a_.set_capabilities(packet_interface::CAPABILITY_SESSION | packet_interface::CAPABILITY_RELIABILITY);
EXPECT_EQ(this->make_sender_()->get_update_interval(), SCHEDULER_DONT_RUN);
auto transport = std::make_unique<TestTransport>(&this->link_a_);
transport->set_update_interval(5000);
transport->setup();
EXPECT_EQ(transport->get_update_interval(), 5000);
}
TEST_F(EntityTransportTest, SessionConnectSendsAllAndLossClearsRemote) {
this->link_a.set_capabilities(packet_interface::CAPABILITY_SESSION);
this->link_b.set_capabilities(packet_interface::CAPABILITY_SESSION);
this->temp.publish_state(7.0f);
auto sender = this->make_sender();
auto receiver = this->make_receiver();
this->link_a_.set_capabilities(packet_interface::CAPABILITY_SESSION);
this->link_b_.set_capabilities(packet_interface::CAPABILITY_SESSION);
this->temp_.publish_state(7.0f);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_();
sender->loop();
this->link_a.deliver_to(this->link_b);
this->link_a.report_session(true);
this->link_a_.deliver_to(this->link_b_);
this->link_a_.report_session(true);
sender->loop();
EXPECT_EQ(this->link_a.sent.size(), 2);
this->link_b.report_session(false);
EXPECT_TRUE(std::isnan(this->remote_temp.get_state()));
EXPECT_FALSE(this->remote_door.has_state());
EXPECT_EQ(this->link_a_.sent.size(), 2);
this->link_b_.report_session(false);
EXPECT_TRUE(std::isnan(this->remote_temp_.get_state()));
EXPECT_FALSE(this->remote_door_.has_state());
}
TEST_F(EntityTransportTest, RefusedDictionaryPartResumes) {
// One entity in each dictionary packet
this->link_a.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 5);
this->temp.publish_state(4.0f);
this->door.publish_state(true);
auto sender = this->make_sender();
auto receiver = this->make_receiver();
this->link_a.budget = 1;
this->link_a_.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 5);
this->temp_.publish_state(4.0f);
this->door_.publish_state(true);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_();
this->link_a_.budget = 1;
sender->loop();
ASSERT_EQ(this->link_a.sent.size(), 1);
this->link_a.budget = SIZE_MAX;
ASSERT_EQ(this->link_a_.sent.size(), 1);
this->link_a_.budget = SIZE_MAX;
sender->loop();
int dictionaries = 0;
for (auto &packet : this->link_a.sent)
for (auto &packet : this->link_a_.sent)
dictionaries += packet[0] == PACKET_DICTIONARY;
EXPECT_EQ(dictionaries, 2); // the first part was not sent again
this->link_a.deliver_to(this->link_b);
EXPECT_EQ(this->remote_temp.get_state(), 4.0f);
EXPECT_TRUE(this->remote_door.state);
this->link_a_.deliver_to(this->link_b_);
EXPECT_EQ(this->remote_temp_.get_state(), 4.0f);
EXPECT_TRUE(this->remote_door_.state);
}
TEST_F(EntityTransportTest, RemoteStatesTimeOut) {
this->temp.publish_state(6.0f);
this->door.publish_state(true);
auto sender = this->make_sender();
auto receiver = this->make_receiver(DEVICE_NAME, 5000);
this->temp_.publish_state(6.0f);
this->door_.publish_state(true);
auto sender = this->make_sender_();
auto receiver = this->make_receiver_(DEVICE_NAME, 5000);
{ LoopBlockingGuard guard{nullptr, nullptr, 1000}; }
sender->loop();
this->link_a.deliver_to(this->link_b);
this->link_a_.deliver_to(this->link_b_);
{ LoopBlockingGuard guard{nullptr, nullptr, 6000}; }
receiver->clear_remote_states_(true);
EXPECT_EQ(this->remote_temp.get_state(), 6.0f);
EXPECT_TRUE(this->remote_door.has_state());
EXPECT_EQ(this->remote_temp_.get_state(), 6.0f);
EXPECT_TRUE(this->remote_door_.has_state());
{ LoopBlockingGuard guard{nullptr, nullptr, 6001}; }
receiver->clear_remote_states_(true);
EXPECT_TRUE(std::isnan(this->remote_temp.get_state()));
EXPECT_FALSE(this->remote_door.has_state());
EXPECT_TRUE(std::isnan(this->remote_temp_.get_state()));
EXPECT_FALSE(this->remote_door_.has_state());
}
TEST_F(EntityTransportTest, IdMatchingOnlyUpToNulNotLearnt) {
auto receiver = this->make_receiver();
auto receiver = this->make_receiver_();
static constexpr uint32_t OTHER_ID = 0x0BADF00D;
Packet dictionary = header(PACKET_DICTIONARY, OTHER_ID);
dictionary.push_back(strlen(DEVICE_NAME));
dictionary.insert(dictionary.end(), DEVICE_NAME, DEVICE_NAME + strlen(DEVICE_NAME));
// The id "temp" followed by a NUL and more bytes, nine in all
dictionary.insert(dictionary.end(), {1, 0, 1, 9, 't', 'e', 'm', 'p', 0, 'a', 'b', 'c', 'd'});
this->link_b.inject(dictionary);
this->link_b_.inject(dictionary);
Packet data = header(PACKET_DATA, OTHER_ID);
data.insert(data.end(), {1, 0, 0x00, 0x00, 0x80, 0x3F});
this->link_b.inject(data);
EXPECT_FALSE(this->remote_temp.has_state());
this->link_b_.inject(data);
EXPECT_FALSE(this->remote_temp_.has_state());
}
TEST_F(EntityTransportTest, PacketTooSmallForIdsFailsSetup) {
// One byte short of a dictionary packet holding the device name and a four character id
this->link_a.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 1 + 4 - 1);
auto sender = this->make_sender();
this->link_a_.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 1 + 4 - 1);
auto sender = this->make_sender_();
EXPECT_TRUE(sender->is_failed());
this->link_a.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 1 + 4);
EXPECT_FALSE(this->make_sender()->is_failed());
this->link_a_.set_max_packet_size(5 + 1 + strlen(DEVICE_NAME) + 3 + 1 + 4);
EXPECT_FALSE(this->make_sender_()->is_failed());
}
} // namespace esphome::entity_transport::testing