mirror of
https://github.com/alexhopeoconnor/arduino-home-assistant.git
synced 2026-10-04 02:48:13 +10:00
test: add MQTT callback publish regressions and mock callback tracking
PubSubClientMock flags inbound callback context and counts publish attempts from callbacks. Entity and core tests assert unsafe callback publishes are rejected by the mock (baseline before HAMqtt deferred publish).
This commit is contained in:
@@ -13,6 +13,8 @@ PubSubClientMock::PubSubClientMock() :
|
||||
_flushedMessagesNb(0),
|
||||
_subscriptions(nullptr),
|
||||
_subscriptionsNb(0),
|
||||
_insideCallback(false),
|
||||
_publishCallsFromCallbackNb(0),
|
||||
callback(nullptr)
|
||||
{
|
||||
|
||||
@@ -119,6 +121,11 @@ bool PubSubClientMock::beginPublish(
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_insideCallback) {
|
||||
_publishCallsFromCallbackNb++;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (_pendingMessage) {
|
||||
delete _pendingMessage;
|
||||
}
|
||||
@@ -239,7 +246,9 @@ void PubSubClientMock::fakeMessage(const char* topic, const char* message)
|
||||
uint8_t data[len];
|
||||
memcpy(data, message, len);
|
||||
|
||||
_insideCallback = true;
|
||||
callback(const_cast<char*>(topic), data, len);
|
||||
_insideCallback = false;
|
||||
}
|
||||
|
||||
void PubSubClientMock::fakeMessage(
|
||||
|
||||
@@ -166,6 +166,15 @@ public:
|
||||
inline const MqttWill& getLastWill() const
|
||||
{ return _lastWill; }
|
||||
|
||||
inline bool isInsideCallback() const
|
||||
{ return _insideCallback; }
|
||||
|
||||
inline uint16_t getPublishCallsFromCallbackNb() const
|
||||
{ return _publishCallsFromCallbackNb; }
|
||||
|
||||
inline void resetPublishCallsFromCallbackNb()
|
||||
{ _publishCallsFromCallbackNb = 0; }
|
||||
|
||||
void clearFlushedMessages();
|
||||
void clearSubscriptions();
|
||||
void fakeMessage(const char* topic, const char* message);
|
||||
@@ -184,6 +193,8 @@ private:
|
||||
uint8_t _subscriptionsNb;
|
||||
MqttConnection _connection;
|
||||
MqttWill _lastWill;
|
||||
bool _insideCallback;
|
||||
uint16_t _publishCallsFromCallbackNb;
|
||||
MQTT_CALLBACK_SIGNATURE;
|
||||
};
|
||||
|
||||
|
||||
@@ -85,6 +85,7 @@ static TestCase tests[] = {
|
||||
TEST_ENTRY(test_SwitchTest_command_off),
|
||||
TEST_ENTRY(test_SwitchTest_command_on),
|
||||
TEST_ENTRY(test_SwitchTest_command_subscription),
|
||||
TEST_ENTRY(test_SwitchTest_callback_publish_attempt_is_rejected),
|
||||
TEST_ENTRY(test_SwitchTest_current_state_setter),
|
||||
TEST_ENTRY(test_SwitchTest_default_entity_id_setter),
|
||||
TEST_ENTRY(test_SwitchTest_default_params),
|
||||
@@ -120,6 +121,7 @@ static TestCase tests[] = {
|
||||
TEST_ENTRY(test_TextTest_publish_nothing_if_retained),
|
||||
TEST_ENTRY(test_TextTest_publish_state),
|
||||
TEST_ENTRY(test_TextTest_publish_state_debounce),
|
||||
TEST_ENTRY(test_TextTest_callback_publish_attempt_is_rejected),
|
||||
TEST_ENTRY(test_TextTest_retain_setter),
|
||||
};
|
||||
|
||||
|
||||
@@ -113,6 +113,7 @@ extern void test_SwitchTest_publish_nothing_if_retained(void);
|
||||
extern void test_SwitchTest_publish_state_off(void);
|
||||
extern void test_SwitchTest_publish_state_on(void);
|
||||
extern void test_SwitchTest_retain_setter(void);
|
||||
extern void test_SwitchTest_callback_publish_attempt_is_rejected(void);
|
||||
extern void test_TextTest_availability(void);
|
||||
extern void test_TextTest_command_callback(void);
|
||||
extern void test_TextTest_command_subscription(void);
|
||||
@@ -131,5 +132,6 @@ extern void test_TextTest_publish_nothing_if_retained(void);
|
||||
extern void test_TextTest_publish_state(void);
|
||||
extern void test_TextTest_publish_state_debounce(void);
|
||||
extern void test_TextTest_retain_setter(void);
|
||||
extern void test_TextTest_callback_publish_attempt_is_rejected(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,11 @@ void onCommandReceived(bool state, HASwitch* caller)
|
||||
lastCommandCallbackCall.caller = caller;
|
||||
}
|
||||
|
||||
void onCommandPublishAttempt(bool state, HASwitch* caller)
|
||||
{
|
||||
TEST_ASSERT_FALSE(caller->setState(state));
|
||||
}
|
||||
|
||||
void test_SwitchTest_invalid_unique_id(void) {
|
||||
prepareTest
|
||||
|
||||
@@ -388,6 +393,19 @@ void test_SwitchTest_command_off(void) {
|
||||
assertCommandCallbackCalled(false, &testSwitch)
|
||||
}
|
||||
|
||||
void test_SwitchTest_callback_publish_attempt_is_rejected(void) {
|
||||
prepareTest
|
||||
|
||||
mock->connectDummy();
|
||||
HASwitch testSwitch(testUniqueId);
|
||||
testSwitch.onCommand(onCommandPublishAttempt);
|
||||
|
||||
mock->fakeMessage(AHATOFSTR(CommandTopic), F("ON"));
|
||||
|
||||
TEST_ASSERT_EQUAL(1, mock->getPublishCallsFromCallbackNb());
|
||||
TEST_ASSERT_EQUAL(0, mock->getFlushedMessagesNb());
|
||||
}
|
||||
|
||||
void test_SwitchTest_different_switch_command(void) {
|
||||
prepareTest
|
||||
|
||||
|
||||
@@ -46,6 +46,11 @@ void onCommandReceived(const char* value, HAText* caller)
|
||||
lastCommandCallbackCall.caller = caller;
|
||||
}
|
||||
|
||||
void onCommandPublishAttempt(const char* value, HAText* caller)
|
||||
{
|
||||
TEST_ASSERT_FALSE(caller->setState(value));
|
||||
}
|
||||
|
||||
void test_TextTest_invalid_unique_id(void) {
|
||||
prepareTest
|
||||
|
||||
@@ -323,6 +328,19 @@ void test_TextTest_command_callback(void) {
|
||||
assertCommandCallbackCalled("hello", &text)
|
||||
}
|
||||
|
||||
void test_TextTest_callback_publish_attempt_is_rejected(void) {
|
||||
prepareTest
|
||||
|
||||
mock->connectDummy();
|
||||
HAText text(testUniqueId);
|
||||
text.onCommand(onCommandPublishAttempt);
|
||||
|
||||
mock->fakeMessage(AHATOFSTR(CommandTopic), F("hello"));
|
||||
|
||||
TEST_ASSERT_EQUAL(1, mock->getPublishCallsFromCallbackNb());
|
||||
TEST_ASSERT_EQUAL(0, mock->getFlushedMessagesNb());
|
||||
}
|
||||
|
||||
void test_TextTest_different_text_command(void) {
|
||||
prepareTest
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@ static TestCase tests[] = {
|
||||
TEST_ENTRY(test_NumberTest_command_number_unsigned),
|
||||
TEST_ENTRY(test_NumberTest_command_number_zero),
|
||||
TEST_ENTRY(test_NumberTest_command_subscription),
|
||||
TEST_ENTRY(test_NumberTest_callback_publish_attempt_is_rejected),
|
||||
TEST_ENTRY(test_NumberTest_current_state_setter_getter_float),
|
||||
TEST_ENTRY(test_NumberTest_current_state_setter_getter_int16),
|
||||
TEST_ENTRY(test_NumberTest_current_state_setter_getter_int32),
|
||||
@@ -226,6 +227,7 @@ static TestCase tests[] = {
|
||||
TEST_ENTRY(test_SelectTest_publish_state_only),
|
||||
TEST_ENTRY(test_SelectTest_retain_setter),
|
||||
TEST_ENTRY(test_SelectTest_single_option),
|
||||
TEST_ENTRY(test_SelectTest_callback_publish_attempt_is_rejected),
|
||||
};
|
||||
|
||||
static const size_t TEST_COUNT = sizeof(tests) / sizeof(tests[0]);
|
||||
|
||||
@@ -23,6 +23,7 @@ extern void test_NumberTest_command_number_signed(void);
|
||||
extern void test_NumberTest_command_number_unsigned(void);
|
||||
extern void test_NumberTest_command_number_zero(void);
|
||||
extern void test_NumberTest_command_subscription(void);
|
||||
extern void test_NumberTest_callback_publish_attempt_is_rejected(void);
|
||||
extern void test_NumberTest_current_state_setter_getter_float(void);
|
||||
extern void test_NumberTest_current_state_setter_getter_int16(void);
|
||||
extern void test_NumberTest_current_state_setter_getter_int32(void);
|
||||
@@ -236,5 +237,6 @@ extern void test_SelectTest_publish_state_none(void);
|
||||
extern void test_SelectTest_publish_state_only(void);
|
||||
extern void test_SelectTest_retain_setter(void);
|
||||
extern void test_SelectTest_single_option(void);
|
||||
extern void test_SelectTest_callback_publish_attempt_is_rejected(void);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -42,6 +42,11 @@ void onCommandReceived(HANumeric number, HANumber* caller)
|
||||
lastCommandCallbackCall.caller = caller;
|
||||
}
|
||||
|
||||
void onCommandPublishAttempt(HANumeric value, HANumber* caller)
|
||||
{
|
||||
TEST_ASSERT_FALSE(caller->setState(value));
|
||||
}
|
||||
|
||||
void test_NumberTest_invalid_unique_id(void) {
|
||||
prepareTest
|
||||
|
||||
@@ -1010,6 +1015,19 @@ void test_NumberTest_command_number_float_p3(void) {
|
||||
assertCommandCallbackCalled(HANumeric(-1.234f, 3), &number)
|
||||
}
|
||||
|
||||
void test_NumberTest_callback_publish_attempt_is_rejected(void) {
|
||||
prepareTest
|
||||
|
||||
mock->connectDummy();
|
||||
HANumber number(testUniqueId);
|
||||
number.onCommand(onCommandPublishAttempt);
|
||||
|
||||
mock->fakeMessage(AHATOFSTR(CommandTopic), F("1234"));
|
||||
|
||||
TEST_ASSERT_EQUAL(1, mock->getPublishCallsFromCallbackNb());
|
||||
TEST_ASSERT_EQUAL(0, mock->getFlushedMessagesNb());
|
||||
}
|
||||
|
||||
void test_NumberTest_command_number_invalid(void) {
|
||||
prepareTest
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@ void onCommandReceived(int8_t index, HASelect* caller)
|
||||
lastCommandCallbackCall.caller = caller;
|
||||
}
|
||||
|
||||
void onCommandPublishAttempt(int8_t index, HASelect* caller)
|
||||
{
|
||||
TEST_ASSERT_FALSE(caller->setState(index));
|
||||
}
|
||||
|
||||
void test_SelectTest_invalid_unique_id(void) {
|
||||
prepareTest
|
||||
|
||||
@@ -477,6 +482,20 @@ void test_SelectTest_command_option_non_existing(void) {
|
||||
assertCommandCallbackNotCalled()
|
||||
}
|
||||
|
||||
void test_SelectTest_callback_publish_attempt_is_rejected(void) {
|
||||
prepareTest
|
||||
|
||||
mock->connectDummy();
|
||||
HASelect select(testUniqueId);
|
||||
select.setOptions("Option A;B;C");
|
||||
select.onCommand(onCommandPublishAttempt);
|
||||
|
||||
mock->fakeMessage(AHATOFSTR(CommandTopic), F("B"));
|
||||
|
||||
TEST_ASSERT_EQUAL(1, mock->getPublishCallsFromCallbackNb());
|
||||
TEST_ASSERT_EQUAL(0, mock->getFlushedMessagesNb());
|
||||
}
|
||||
|
||||
void test_SelectTest_different_select_command(void) {
|
||||
prepareTest
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ static TestCase tests[] = {
|
||||
TEST_ENTRY(test_MqttTest_reconnect_interval_default_value),
|
||||
TEST_ENTRY(test_MqttTest_reconnect_interval_setter),
|
||||
TEST_ENTRY(test_MqttTest_reconnect_interval_throttles_attempts),
|
||||
TEST_ENTRY(test_MqttTest_publish_attempt_from_message_callback_is_rejected),
|
||||
|
||||
TEST_ENTRY(test_DeviceTest_default_unique_id),
|
||||
TEST_ENTRY(test_DeviceTest_unique_id_constructor_char),
|
||||
|
||||
@@ -17,6 +17,7 @@ extern void test_MqttTest_maximum_number_of_device_types(void);
|
||||
extern void test_MqttTest_reconnect_interval_default_value(void);
|
||||
extern void test_MqttTest_reconnect_interval_setter(void);
|
||||
extern void test_MqttTest_reconnect_interval_throttles_attempts(void);
|
||||
extern void test_MqttTest_publish_attempt_from_message_callback_is_rejected(void);
|
||||
|
||||
extern void test_DeviceTest_default_unique_id(void);
|
||||
extern void test_DeviceTest_unique_id_constructor_char(void);
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
|
||||
static const char* testDeviceId = "testDevice";
|
||||
static const char* testUniqueId = "uniqueId";
|
||||
static HAMqtt* activeMqtt = nullptr;
|
||||
static bool messageCallbackCalled = false;
|
||||
|
||||
const char ComponentNameStr[] PROGMEM = {"componentName"};
|
||||
|
||||
@@ -20,6 +22,17 @@ protected:
|
||||
}
|
||||
};
|
||||
|
||||
void onMessagePublishAttempt(const char* topic, const uint8_t* payload, uint16_t length)
|
||||
{
|
||||
(void)topic;
|
||||
(void)payload;
|
||||
(void)length;
|
||||
|
||||
messageCallbackCalled = true;
|
||||
TEST_ASSERT_NOT_NULL(activeMqtt);
|
||||
TEST_ASSERT_FALSE(activeMqtt->publish("testData/testDevice/echo", "1", false));
|
||||
}
|
||||
|
||||
void test_MqttTest_maximum_number_of_device_types(void)
|
||||
{
|
||||
HADevice device(testDeviceId);
|
||||
@@ -70,3 +83,26 @@ void test_MqttTest_reconnect_interval_throttles_attempts(void)
|
||||
mqtt.loop();
|
||||
TEST_ASSERT_EQUAL_UINT16(2, mock->getConnectCallsNb());
|
||||
}
|
||||
|
||||
void test_MqttTest_publish_attempt_from_message_callback_is_rejected(void)
|
||||
{
|
||||
PubSubClientMock* mock = new PubSubClientMock();
|
||||
HADevice device(testDeviceId);
|
||||
HAMqtt mqtt(mock, device);
|
||||
mqtt.setDataPrefix("testData");
|
||||
mqtt.begin("testHost", "testUser", "testPass");
|
||||
mock->connectDummy();
|
||||
|
||||
activeMqtt = &mqtt;
|
||||
messageCallbackCalled = false;
|
||||
mock->resetPublishCallsFromCallbackNb();
|
||||
mqtt.onMessage(onMessagePublishAttempt);
|
||||
|
||||
mock->fakeMessage("testData/testDevice/input", "1");
|
||||
|
||||
TEST_ASSERT_TRUE(messageCallbackCalled);
|
||||
TEST_ASSERT_EQUAL_UINT16(1, mock->getPublishCallsFromCallbackNb());
|
||||
TEST_ASSERT_EQUAL_UINT8(0, mock->getFlushedMessagesNb());
|
||||
|
||||
activeMqtt = nullptr;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user