// Host tests of the messages: what every kind of AlexaStatusMessage carries, byte for byte, and the topic it is // published on (src/AlexaStatusMessage.cpp, the messages of src/AlexaDevice.cpp). The transport of the devices is // a fake that keeps what would have been published. // pio test -e native #include #include #include #include #include #include #include #include "AlexaDevice.h" #include "AlexaLog.h" class FakeTransport : public AlexaTransport { public: struct Message { std::string topic; std::string json; }; std::vector sent; AlexaSendResult publish(const char *topic, JsonDocument &doc) override { Message message; message.topic = topic; serializeJson(doc, message.json); sent.push_back(message); return AlexaSendResult::OK; } void timestamp(char *buffer, size_t size) override { snprintf(buffer, size, "2026-09-28T13:05:09Z"); } }; class CapturedLog : public Print { public: std::string text; size_t write(const uint8_t *buffer, size_t size) override { text.append(reinterpret_cast(buffer), size); return size; } }; static void assertLogged(const CapturedLog &log, const char *expected) { TEST_ASSERT_NOT_NULL_MESSAGE(strstr(log.text.c_str(), expected), log.text.c_str()); } static void assertIsUuid(const char *id) { TEST_ASSERT_EQUAL_MESSAGE(36, strlen(id), id); for (int i = 0; i < 36; i++) { if (i == 8 || i == 13 || i == 18 || i == 23) { TEST_ASSERT_EQUAL_MESSAGE('-', id[i], id); } else { TEST_ASSERT_TRUE_MESSAGE(isdigit(id[i]) || (id[i] >= 'a' && id[i] <= 'f'), id); } } TEST_ASSERT_EQUAL_MESSAGE('4', id[14], id); TEST_ASSERT_NOT_NULL_MESSAGE(strchr("89ab", id[19]), id); } // The message as it was published. Its messageId is random: it is checked for its shape and replaced by "id". static std::string published(const FakeTransport::Message &message) { JsonDocument doc; TEST_ASSERT_TRUE(deserializeJson(doc, message.json) == DeserializationError::Ok); assertIsUuid(doc["event"]["header"]["messageId"] | ""); doc["event"]["header"]["messageId"] = "id"; std::string text; serializeJson(doc, text); return text; } #define HEADER_OF(ns, name) "\"header\":{\"namespace\":\"" ns "\",\"name\":\"" name "\",\"payloadVersion\":\"3\",\"messageId\":\"id\"" #define TOKEN ",\"correlationToken\":\"token-of-the-directive\"}" #define ENDPOINT ",\"endpoint\":{\"endpointId\":\"ESP-01\"}" #define SAMPLED ",\"timeOfSample\":\"2026-09-28T13:05:09Z\",\"uncertaintyInMilliseconds\":0}" #define HEALTH_OK "{\"namespace\":\"Alexa.EndpointHealth\",\"name\":\"connectivity\",\"value\":{\"value\":\"OK\"}" SAMPLED #define POWER_ON "{\"namespace\":\"Alexa.PowerController\",\"name\":\"powerState\",\"value\":\"ON\"" SAMPLED static const char TOKEN_OF_THE_DIRECTIVE[] = "token-of-the-directive"; // What a handler is given, for the answers a directive builds static AlexaDirective directiveFor(AlexaDevice &device, const char *ns, const char *name) { AlexaDirective directive; directive.device = &device; directive.capability = nullptr; directive.type = AlexaInterfaceType::UNKNOWN; directive.ns = ns; directive.name = name; directive.instance = ""; directive.correlationToken = TOKEN_OF_THE_DIRECTIVE; return directive; } void setUp(void) { AlexaLog::setOutput(nullptr); } void tearDown(void) {} void test_response_carries_the_token_and_the_properties(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); AlexaDirective directive = directiveFor(lamp, "Alexa.PowerController", "TurnOn"); AlexaStatusMessage message = directive.response(); TEST_ASSERT_TRUE(AlexaMessageKind::RESPONSE == message.getKind()); TEST_ASSERT_TRUE(message.addHealthProp(EndpointHealth::OK).addPowerControllerProp(PowerController::ON).send()); TEST_ASSERT_EQUAL(1, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/alexaResponce", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "Response") TOKEN ENDPOINT ",\"payload\":{}}," "\"context\":{\"properties\":[" HEALTH_OK "," POWER_ON "]}}", published(transport.sent[0]).c_str()); } void test_state_report_carries_the_token_and_the_properties(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); AlexaDirective directive = directiveFor(lamp, "Alexa", "ReportState"); TEST_ASSERT_TRUE(directive.stateReport().addHealthProp(EndpointHealth::OK).addPowerControllerProp(PowerController::ON).send()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/alexaResponce", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "StateReport") TOKEN ENDPOINT ",\"payload\":{}}," "\"context\":{\"properties\":[" HEALTH_OK "," POWER_ON "]}}", published(transport.sent[0]).c_str()); } void test_names_of_1x_build_the_same_properties(void) { FakeTransport transport; AlexaDevice blind("Blind", "root", "ESP-01", &transport); blind.buildStatusMessage(TOKEN_OF_THE_DIRECTIVE, true) .AddHealthProp(EndpointHealth::OK) .AddPowerControllerProp(PowerController::ON) .AddBrightnessControllerProp(40) .AddColorTemperatureControllerProp(2700) .AddToggleControllerProp(PowerController::OFF, "ESP-01.Toggle") .send(); blind.response(TOKEN_OF_THE_DIRECTIVE) .addHealthProp(EndpointHealth::OK) .addPowerControllerProp(PowerController::ON) .addBrightnessControllerProp(40) .addColorTemperatureControllerProp(2700) .addToggleControllerProp("ESP-01.Toggle", PowerController::OFF) .send(); TEST_ASSERT_EQUAL(2, transport.sent.size()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "Response") TOKEN ENDPOINT ",\"payload\":{}}," "\"context\":{\"properties\":[" HEALTH_OK "," POWER_ON "," "{\"namespace\":\"Alexa.BrightnessController\",\"name\":\"brightness\",\"value\":40" SAMPLED "," "{\"namespace\":\"Alexa.ColorTemperatureController\",\"name\":\"colorTemperatureInKelvin\",\"value\":2700" SAMPLED "," "{\"namespace\":\"Alexa.ToggleController\",\"name\":\"toggleState\",\"instance\":\"ESP-01.Toggle\",\"value\":\"OFF\"" SAMPLED "]}}", published(transport.sent[0]).c_str()); TEST_ASSERT_EQUAL_STRING(published(transport.sent[0]).c_str(), published(transport.sent[1]).c_str()); } void test_temperature_is_reported_in_the_scale_it_is_given_in(void) { FakeTransport transport; AlexaDevice sensor("Sensor", "root", "ESP-01", &transport); sensor.response(TOKEN_OF_THE_DIRECTIVE) .AddTemperatureSensorProp(TemperatureSensorScale::FAHRENHEIT, 69) .addTemperatureSensorProp(20.5f) .addTemperatureSensorProp(293.5f, TemperatureSensorScale::KELVIN, 500) .send(); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "Response") TOKEN ENDPOINT ",\"payload\":{}}," "\"context\":{\"properties\":[" "{\"namespace\":\"Alexa.TemperatureSensor\",\"name\":\"temperature\",\"value\":{\"value\":69,\"scale\":\"FAHRENHEIT\"}" SAMPLED "," "{\"namespace\":\"Alexa.TemperatureSensor\",\"name\":\"temperature\",\"value\":{\"value\":20.5,\"scale\":\"CELSIUS\"}" SAMPLED "," "{\"namespace\":\"Alexa.TemperatureSensor\",\"name\":\"temperature\",\"value\":{\"value\":293.5,\"scale\":\"KELVIN\"}," "\"timeOfSample\":\"2026-09-28T13:05:09Z\",\"uncertaintyInMilliseconds\":500}" "]}}", published(transport.sent[0]).c_str()); } void test_property_of_the_sketch_gets_the_time_in_place_of_the_placeholder(void) { FakeTransport transport; AlexaDevice lock("Lock", "root", "ESP-01", &transport); JsonDocument property; property["namespace"] = "Alexa.LockController"; property["name"] = "lockState"; property["value"] = "LOCKED"; property["timeOfSample"] = "{REPLACE_WITH_DATETIME}"; property["uncertaintyInMilliseconds"] = 0; lock.response(TOKEN_OF_THE_DIRECTIVE).addContextProp(property.as()).send(); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "Response") TOKEN ENDPOINT ",\"payload\":{}}," "\"context\":{\"properties\":[{\"namespace\":\"Alexa.LockController\",\"name\":\"lockState\",\"value\":\"LOCKED\"" SAMPLED "]}}", published(transport.sent[0]).c_str()); } void test_deferred_response_has_the_estimate_and_no_context(void) { FakeTransport transport; AlexaDevice lock("Lock", "root", "ESP-01", &transport); AlexaDirective directive = directiveFor(lock, "Alexa.LockController", "Lock"); AlexaStatusMessage message = directive.deferred(7); TEST_ASSERT_TRUE(AlexaMessageKind::DEFERRED_RESPONSE == message.getKind()); TEST_ASSERT_TRUE(message.addHealthProp(EndpointHealth::OK).send()); TEST_ASSERT_TRUE(directive.deferred().send()); TEST_ASSERT_EQUAL(2, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/alexaResponce", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "DeferredResponse") TOKEN ENDPOINT ",\"payload\":{\"estimatedDeferralInSeconds\":7}}}", published(transport.sent[0]).c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "DeferredResponse") TOKEN ENDPOINT ",\"payload\":{}}}", published(transport.sent[1]).c_str()); } void test_answer_after_a_deferred_response_goes_to_the_deferred_topic(void) { FakeTransport transport; AlexaDevice lock("Lock", "root", "ESP-01", &transport); TEST_ASSERT_TRUE(lock.response(TOKEN_OF_THE_DIRECTIVE).addHealthProp(EndpointHealth::OK).sendAsync()); TEST_ASSERT_TRUE(lock.response(TOKEN_OF_THE_DIRECTIVE) .asErrorResponse(AlexaErrorType::DEVICE_STUCK, "The bolt does not move") .sendAsync()); TEST_ASSERT_EQUAL(2, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/deferredResponse", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "Response") TOKEN ENDPOINT ",\"payload\":{}}," "\"context\":{\"properties\":[" HEALTH_OK "]}}", published(transport.sent[0]).c_str()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/deferredResponse", transport.sent[1].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "ErrorResponse") TOKEN ENDPOINT ",\"payload\":{\"type\":\"DEVICE_STUCK\",\"message\":\"The bolt does not move\"}}}", published(transport.sent[1]).c_str()); } void test_send_async_refuses_what_is_no_answer_to_a_directive(void) { CapturedLog log; AlexaLog::setOutput(&log); FakeTransport transport; AlexaDevice lock("Lock", "root", "ESP-01", &transport); AlexaDirective directive = directiveFor(lock, "Alexa.LockController", "Lock"); TEST_ASSERT_FALSE(directive.deferred(7).sendAsync()); TEST_ASSERT_FALSE(lock.changeReport(AlexaCause::PHYSICAL_INTERACTION).addHealthProp(EndpointHealth::OK).sendAsync()); TEST_ASSERT_EQUAL(0, transport.sent.size()); assertLogged(log, "error: message for ESP-01 not sent: sendAsync() is for the Response, StateReport or " "ErrorResponse that follows a DeferredResponse, use send()"); } void test_change_report_keeps_the_changed_properties_apart_from_the_others(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); AlexaStatusMessage message = lamp.changeReport(AlexaCause::PHYSICAL_INTERACTION); TEST_ASSERT_TRUE(AlexaMessageKind::CHANGE_REPORT == message.getKind()); TEST_ASSERT_TRUE(message.addPowerControllerProp(PowerController::ON).context().addHealthProp(EndpointHealth::OK).send()); TEST_ASSERT_EQUAL(1, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/changeReport", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "ChangeReport") "}" ENDPOINT ",\"payload\":{\"change\":{\"cause\":{\"type\":\"PHYSICAL_INTERACTION\"},\"properties\":[" POWER_ON "]}}}," "\"context\":{\"properties\":[" HEALTH_OK "]}}", published(transport.sent[0]).c_str()); } void test_change_report_names_its_cause(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); const AlexaCause causes[] = {AlexaCause::APP_INTERACTION, AlexaCause::PERIODIC_POLL, AlexaCause::PHYSICAL_INTERACTION, AlexaCause::VOICE_INTERACTION}; const char *const names[] = {"APP_INTERACTION", "PERIODIC_POLL", "PHYSICAL_INTERACTION", "VOICE_INTERACTION"}; for (int i = 0; i < 4; i++) { lamp.changeReport(causes[i]).addPowerControllerProp(PowerController::ON).send(); JsonDocument report; TEST_ASSERT_TRUE(deserializeJson(report, transport.sent[i].json) == DeserializationError::Ok); TEST_ASSERT_EQUAL_STRING(names[i], report["event"]["payload"]["change"]["cause"]["type"] | ""); TEST_ASSERT_EQUAL(0, report["context"]["properties"].size()); } } void test_change_report_without_a_changed_property_is_refused_with_an_error(void) { CapturedLog log; AlexaLog::setOutput(&log); FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); TEST_ASSERT_FALSE(lamp.changeReport(AlexaCause::PERIODIC_POLL).context().addHealthProp(EndpointHealth::OK).send()); TEST_ASSERT_EQUAL(0, transport.sent.size()); assertLogged(log, "error: change report for ESP-01 not sent: it has no property that changed, add one before context()"); } void test_error_response_has_the_type_the_message_and_what_the_sketch_adds(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); AlexaDirective directive = directiveFor(lamp, "Alexa.BrightnessController", "SetBrightness"); AlexaStatusMessage message = directive.error(AlexaErrorType::VALUE_OUT_OF_RANGE, "The brightness is 0 to 100"); TEST_ASSERT_TRUE(AlexaMessageKind::ERROR_RESPONSE == message.getKind()); message.payload()["validRange"]["minimumValue"] = 0; message.payload()["validRange"]["maximumValue"] = 100; TEST_ASSERT_TRUE(message.addHealthProp(EndpointHealth::OK).send()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/alexaResponce", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "ErrorResponse") TOKEN ENDPOINT ",\"payload\":{\"type\":\"VALUE_OUT_OF_RANGE\",\"message\":\"The brightness is 0 to 100\"," "\"validRange\":{\"minimumValue\":0,\"maximumValue\":100}}}}", published(transport.sent[0]).c_str()); } void test_error_of_a_thermostat_is_sent_in_the_namespace_of_the_thermostat(void) { FakeTransport transport; AlexaDevice thermostat("Thermostat", "root", "ESP-01", &transport); AlexaDirective directive = directiveFor(thermostat, "Alexa.ThermostatController", "SetTargetTemperature"); directive.error(AlexaErrorType::REQUESTED_SETPOINTS_TOO_CLOSE, "The setpoints are 2 degrees apart or more").send(); directive.error(AlexaErrorType::UNWILLING_TO_SET_VALUE, "The window is open").send(); directive.error(AlexaErrorType::VALUE_OUT_OF_RANGE, "10 to 30").send(); directive.error(AlexaErrorType::ALREADY_IN_OPERATION, "Heating").send(); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa.ThermostatController", "ErrorResponse") TOKEN ENDPOINT ",\"payload\":{\"type\":\"REQUESTED_SETPOINTS_TOO_CLOSE\",\"message\":\"The setpoints are 2 degrees apart or more\"}}}", published(transport.sent[0]).c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa.ThermostatController", "ErrorResponse") TOKEN ENDPOINT ",\"payload\":{\"type\":\"UNWILLING_TO_SET_VALUE\",\"message\":\"The window is open\"}}}", published(transport.sent[1]).c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "ErrorResponse") TOKEN ENDPOINT ",\"payload\":{\"type\":\"VALUE_OUT_OF_RANGE\",\"message\":\"10 to 30\"}}}", published(transport.sent[2]).c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa", "ErrorResponse") TOKEN ENDPOINT ",\"payload\":{\"type\":\"ALREADY_IN_OPERATION\",\"message\":\"Heating\"}}}", published(transport.sent[3]).c_str()); } void test_scene_answers_with_its_own_events(void) { FakeTransport transport; AlexaDevice scene("Movie scene", "root", "ESP-01", &transport); scene.addCapability(AlexaInterfaces::SceneController); AlexaDirective directive = directiveFor(scene, "Alexa.SceneController", "Activate"); AlexaStatusMessage message = directive.sceneStarted(); TEST_ASSERT_TRUE(AlexaMessageKind::SCENE_EVENT == message.getKind()); TEST_ASSERT_TRUE(message.send()); TEST_ASSERT_TRUE(directive.sceneStopped().send()); TEST_ASSERT_EQUAL(2, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/alexaResponce", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa.SceneController", "ActivationStarted") TOKEN ENDPOINT ",\"payload\":{\"cause\":{\"type\":\"VOICE_INTERACTION\"},\"timestamp\":\"2026-09-28T13:05:09Z\"}},\"context\":{}}", published(transport.sent[0]).c_str()); TEST_ASSERT_EQUAL_STRING("root/ESP-01/alexaResponce", transport.sent[1].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa.SceneController", "DeactivationStarted") TOKEN ENDPOINT ",\"payload\":{\"cause\":{\"type\":\"VOICE_INTERACTION\"},\"timestamp\":\"2026-09-28T13:05:09Z\"}},\"context\":{}}", published(transport.sent[1]).c_str()); } void test_doorbell_press_goes_to_the_event_topic(void) { FakeTransport transport; AlexaDevice doorbell("Doorbell", "root", "ESP-01", &transport); doorbell.addCapability(AlexaInterfaces::DoorbellEventSource); AlexaStatusMessage message = doorbell.doorbellPress(); TEST_ASSERT_TRUE(AlexaMessageKind::EVENT == message.getKind()); TEST_ASSERT_TRUE(message.send()); TEST_ASSERT_EQUAL(1, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/event", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa.DoorbellEventSource", "DoorbellPress") "}" ENDPOINT ",\"payload\":{\"cause\":{\"type\":\"PHYSICAL_INTERACTION\"},\"timestamp\":\"2026-09-28T13:05:09Z\"}},\"context\":{}}", published(transport.sent[0]).c_str()); } void test_event_of_an_interface_the_device_lacks_is_reported(void) { CapturedLog log; AlexaLog::setOutput(&log); FakeTransport transport; AlexaDevice button("Button", "root", "ESP-01", &transport); button.addCapability(AlexaInterfaces::PowerController); AlexaStatusMessage message = button.event(AlexaInterfaces::WakeOnLANController, "WakeUp"); assertLogged(log, "error: ESP-01: event of an interface the device does not have, Alexa will drop it: " "add the capability with addCapability()"); TEST_ASSERT_TRUE(message.send()); TEST_ASSERT_EQUAL_STRING("root/event", transport.sent[0].topic.c_str()); TEST_ASSERT_EQUAL_STRING( "{\"event\":{" HEADER_OF("Alexa.WakeOnLANController", "WakeUp") "}" ENDPOINT ",\"payload\":{}},\"context\":{}}", published(transport.sent[0]).c_str()); } void test_every_message_has_an_id_of_its_own(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); lamp.response(TOKEN_OF_THE_DIRECTIVE).send(); lamp.response(TOKEN_OF_THE_DIRECTIVE).send(); JsonDocument first; JsonDocument second; TEST_ASSERT_TRUE(deserializeJson(first, transport.sent[0].json) == DeserializationError::Ok); TEST_ASSERT_TRUE(deserializeJson(second, transport.sent[1].json) == DeserializationError::Ok); assertIsUuid(first["event"]["header"]["messageId"] | ""); TEST_ASSERT_TRUE(strcmp(first["event"]["header"]["messageId"] | "", second["event"]["header"]["messageId"] | "") != 0); } static void sendsAChangeReportAndNoAnswer(AlexaDirective &directive) { directive.device->changeReport(AlexaCause::VOICE_INTERACTION).addPowerControllerProp(PowerController::ON).send(); } void test_change_report_of_a_handler_is_not_its_answer(void) { CapturedLog log; AlexaLog::setOutput(&log); FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); lamp.addCapability(AlexaInterfaces::PowerController); lamp.onDirective(sendsAChangeReportAndNoAnswer); JsonDocument message; message["header"]["namespace"] = "Alexa.PowerController"; message["header"]["name"] = "TurnOn"; message["header"]["correlationToken"] = TOKEN_OF_THE_DIRECTIVE; message["endpoint"]["endpointId"] = "ESP-01"; message["payload"].to(); lamp.handleDirective(message); TEST_ASSERT_EQUAL(1, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("root/changeReport", transport.sent[0].topic.c_str()); assertLogged(log, "error: ESP-01: the handler sent no answer to Alexa.PowerController.TurnOn"); } void test_message_is_sent_once(void) { FakeTransport transport; AlexaDevice lamp("Lamp", "root", "ESP-01", &transport); AlexaStatusMessage message = lamp.response(TOKEN_OF_THE_DIRECTIVE); TEST_ASSERT_TRUE(message.addHealthProp(EndpointHealth::OK).send()); message.send(); TEST_ASSERT_EQUAL(2, transport.sent.size()); TEST_ASSERT_EQUAL_STRING("null", transport.sent[1].json.c_str()); // the bridge refuses it as empty } int main(int, char **) { UNITY_BEGIN(); RUN_TEST(test_response_carries_the_token_and_the_properties); RUN_TEST(test_state_report_carries_the_token_and_the_properties); RUN_TEST(test_names_of_1x_build_the_same_properties); RUN_TEST(test_temperature_is_reported_in_the_scale_it_is_given_in); RUN_TEST(test_property_of_the_sketch_gets_the_time_in_place_of_the_placeholder); RUN_TEST(test_deferred_response_has_the_estimate_and_no_context); RUN_TEST(test_answer_after_a_deferred_response_goes_to_the_deferred_topic); RUN_TEST(test_send_async_refuses_what_is_no_answer_to_a_directive); RUN_TEST(test_change_report_keeps_the_changed_properties_apart_from_the_others); RUN_TEST(test_change_report_names_its_cause); RUN_TEST(test_change_report_without_a_changed_property_is_refused_with_an_error); RUN_TEST(test_error_response_has_the_type_the_message_and_what_the_sketch_adds); RUN_TEST(test_error_of_a_thermostat_is_sent_in_the_namespace_of_the_thermostat); RUN_TEST(test_scene_answers_with_its_own_events); RUN_TEST(test_doorbell_press_goes_to_the_event_topic); RUN_TEST(test_event_of_an_interface_the_device_lacks_is_reported); RUN_TEST(test_every_message_has_an_id_of_its_own); RUN_TEST(test_change_report_of_a_handler_is_not_its_answer); RUN_TEST(test_message_is_sent_once); return UNITY_END(); }