Log: one line for whatever is dropped, a level the build lacks is reported, no false alarm for a 1.x handler

Audit of the paths that drop or refuse something. registerEvent() dropped an eleventh
handler and a second one for the same name without a word: the name now gets the new
handler, an eleventh name and a call without a name or a function print an error. A
device that cannot be announced printed two lines, one without its id; it prints one.
setLogLevel() above ALEX2ESP_LOG_MAX prints an error that names the build flag.
"the handler sent no answer" is an error for a handler of onDirective() only: a 1.x
handler may answer from a later loop(), its silence is a line at DEBUG.
getDevice() returning nullptr and printMemoryInfo() are documented; keywords.txt has
the 2.0 names and loses three that were removed.
basicLight: static RAM 30,520 B, flash 333,989 B (+600); ALEX2ESP_LOG_MAX=0 30,496 /
327,953, =3 30,520 / 334,477; five examples, log0 and log3 build with 0 warnings.
Host tests: 135 (dispatch 11 -> 16).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
David 2026-09-28 21:10:19 +00:00
parent 77156ffbe5
commit 6e15226ce0
10 changed files with 216 additions and 26 deletions

View file

@ -1,6 +1,6 @@
// Host tests of dispatch: which device and which capability a directive reaches, what the handler is given, what
// a device holds, and what the device answers itself (src/AlexaDevice.cpp). The transport of the devices is a
// fake that keeps what would have been published.
// a device holds, and what the device answers itself (src/AlexaDevice.cpp); and of the log level (src/AlexaLog.cpp).
// The transport of the devices is a fake that keeps what would have been published.
// pio test -e native
#include <unity.h>
#include <ArduinoJson.h>
@ -143,6 +143,7 @@ static void assertErrorResponse(const FakeTransport::Message &message, const cha
void setUp(void)
{
AlexaLog::setOutput(nullptr);
AlexaLog::setLevel(static_cast<AlexaLogLevel>(2));
seen = Seen();
handlerAnswers = true;
eventCalls = 0;
@ -391,6 +392,101 @@ void test_handler_that_sends_nothing_for_a_capability_of_the_device_is_reported(
assertLogged(log, "error: ESP-01: the handler sent no answer to Alexa.PowerController.TurnOn");
}
void test_handler_of_1x_that_answers_later_is_not_reported(void)
{
CapturedLog log;
AlexaLog::setOutput(&log);
AlexaLog::setLevel(static_cast<AlexaLogLevel>(1));
FakeTransport transport;
AlexaDevice lamp("Lamp", "root", "ESP-01", &transport);
lamp.addCapability(AlexaInterfaceType::POWER_CONTROLLER);
lamp.registerEvent("Event", onEvent);
JsonDocument message;
directiveFor(message, "ESP-01", "Alexa.PowerController", "TurnOn");
lamp.handleDirective(message);
TEST_ASSERT_EQUAL(1, eventCalls);
TEST_ASSERT_EQUAL(0, transport.sent.size());
TEST_ASSERT_EQUAL_STRING("", log.text.c_str());
// The answer from loop(), with the token the sketch kept
TEST_ASSERT_TRUE(lamp.buildStatusMessage("token-of-the-directive", true).AddPowerControllerProp(PowerController::ON).send());
TEST_ASSERT_EQUAL(1, transport.sent.size());
}
void test_second_handler_for_an_event_replaces_the_first(void)
{
FakeTransport transport;
AlexaDevice lamp("Lamp", "root", "ESP-01", &transport);
lamp.registerEvent("Event", onReportState);
lamp.registerEvent("Event", onEvent);
JsonDocument message;
lamp.triggerEvent("Event", message, AlexaInterfaceType::UNKNOWN);
TEST_ASSERT_EQUAL(1, eventCalls);
TEST_ASSERT_EQUAL(0, reportStateCalls);
}
void test_one_handler_more_than_a_device_keeps_is_refused_with_an_error(void)
{
CapturedLog log;
FakeTransport transport;
AlexaDevice lamp("Lamp", "root", "ESP-01", &transport);
static const char *const NAMES[MAX_EVENTS] = {"Event", "ReportState", "DirectiveReceived", "A", "B",
"C", "D", "E", "F", "G"};
for (const char *name : NAMES)
{
lamp.registerEvent(name, onEvent);
}
AlexaLog::setOutput(&log);
lamp.registerEvent("Eleventh", onEvent);
lamp.registerEvent(nullptr, onEvent);
lamp.registerEvent("Event", nullptr);
assertLogged(log, "error: ESP-01: handler for Eleventh not registered: the device has 10 already (MAX_EVENTS)");
assertLogged(log, "error: ESP-01: handler not registered: registerEvent() takes the name of the event and a function");
JsonDocument message;
TEST_ASSERT_FALSE(lamp.triggerEvent("Eleventh", message, AlexaInterfaceType::UNKNOWN, false));
TEST_ASSERT_TRUE(lamp.triggerEvent("Event", message, AlexaInterfaceType::UNKNOWN, false));
TEST_ASSERT_TRUE(lamp.triggerEvent("G", message, AlexaInterfaceType::UNKNOWN, false));
TEST_ASSERT_EQUAL(2, eventCalls);
}
void test_level_above_the_ceiling_of_the_build_is_reported(void)
{
CapturedLog log;
AlexaLog::setOutput(&log);
AlexaLog::setLevel(static_cast<AlexaLogLevel>(ALEX2ESP_LOG_MAX));
TEST_ASSERT_EQUAL_STRING("", log.text.c_str());
AlexaLog::setLevel(static_cast<AlexaLogLevel>(ALEX2ESP_LOG_MAX + 1));
char expected[160];
snprintf(expected, sizeof(expected),
"error: log level %d asked for, the lines of this build end at level %d: build with -DALEX2ESP_LOG_MAX=%d",
ALEX2ESP_LOG_MAX + 1, ALEX2ESP_LOG_MAX, ALEX2ESP_LOG_MAX + 1);
assertLogged(log, expected);
}
void test_level_none_prints_nothing(void)
{
CapturedLog log;
AlexaLog::setOutput(&log);
AlexaLog::setLevel(static_cast<AlexaLogLevel>(0));
FakeTransport transport;
AlexaDevice lamp("Lamp", "root", "ESP-01", &transport);
JsonDocument message;
directiveFor(message, "ESP-01", "Alexa.PowerController", "TurnOn");
lamp.handleDirective(message);
// The answer is sent all the same
assertErrorResponse(transport.sent[0], "root/ESP-01/alexaResponce");
TEST_ASSERT_EQUAL_STRING("", log.text.c_str());
}
void test_handlers_of_1x_get_report_state_and_the_type_of_every_other_directive(void)
{
FakeTransport transport;
@ -433,6 +529,11 @@ int main(int, char **)
RUN_TEST(test_answer_of_the_handler_to_an_interface_the_device_lacks_is_the_only_answer);
RUN_TEST(test_device_without_a_handler_answers_with_invalid_directive);
RUN_TEST(test_handler_that_sends_nothing_for_a_capability_of_the_device_is_reported);
RUN_TEST(test_handler_of_1x_that_answers_later_is_not_reported);
RUN_TEST(test_second_handler_for_an_event_replaces_the_first);
RUN_TEST(test_one_handler_more_than_a_device_keeps_is_refused_with_an_error);
RUN_TEST(test_level_above_the_ceiling_of_the_build_is_reported);
RUN_TEST(test_level_none_prints_nothing);
RUN_TEST(test_handlers_of_1x_get_report_state_and_the_type_of_every_other_directive);
return UNITY_END();
}