/* * Copyright (c) 2022 RenĂ© Beckmann * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include /* for ARRAY_SIZE */ #include #include LOG_MODULE_REGISTER(test); #define CLIENTID MQTT_UTF8_LITERAL("zephyr") #define BUFFER_SIZE 128 struct mqtt_sn_decode_test { uint8_t *data; size_t datasz; char *name; struct mqtt_sn_param expected; }; #define MQTT_SN_DECODE_TEST(dataset) \ .data = dataset, .datasz = sizeof(dataset), .name = STRINGIFY(dataset) /* advertise gw id 42, duration 0xDEAD */ static uint8_t advertise[] = { 5, 0x00, 42, 0xDE, 0xAD }; /* searchgw with radius 1 */ static uint8_t searchgw[] = { 3, 0x01, 1 }; /* gwinfo gw id 42, address 127.0.0.1 */ static uint8_t gwinfo[] = { 7, 0x02, 42, 127, 0, 0, 1 }; /* connect with flags [will, clean_session], duration 300, client id "zephyrclient" */ static uint8_t connect[] = { 18, 0x04, 0x0C, 0x01, 1, 44, 'z', 'e', 'p', 'h', 'y', 'r', 'c', 'l', 'i', 'e', 'n', 't' }; /* connack with return code accepted */ static uint8_t connack1[] = { 3, 0x05, 0x00 }; /* connack with extended length field and return code rejected - invalid topic id */ static uint8_t connack2[] = { 0x01, 0, 5, 0x05, 0x02 }; /* empty message */ static uint8_t willtopicreq[] = { 2, 0x06 }; /* willtopic with flags [qos 1, retain], topic "/zephyr" */ static uint8_t willtopic[] = { 10, 0x07, 0x30, '/', 'z', 'e', 'p', 'h', 'y', 'r' }; /* empty message */ static uint8_t willmsgreq[] = { 2, 0x08 }; /* willmsg with msg "mywill" */ static uint8_t willmsg[] = { 8, 0x09, 'm', 'y', 'w', 'i', 'l', 'l' }; /* registration with topic ID 0x1A1B, msg ID 0x1C1D, topic "/zephyr" */ static uint8_t reg[] = { 13, 0x0A, 0x1A, 0x1B, 0x1C, 0x1D, '/', 'z', 'e', 'p', 'h', 'y', 'r' }; /* registration with topic ID 0x0000, msg ID 0x1C1D, topic "/zephyr" */ static uint8_t reg_client[] = { 13, 0x0A, 0x00, 0x00, 0x1C, 0x1D, '/', 'z', 'e', 'p', 'h', 'y', 'r' }; /* registration ack with topic ID 0x1A1B, msg ID 0x1C1D, return code accepted */ static uint8_t regack[] = { 7, 0x0B, 0x1A, 0x1B, 0x1C, 0x1D, 0 }; /* publish message with flags [DUP, QOS2, Retain, Short Topic Type], topic ID "RB", msg ID 0x1C1D, * data "zephyr" */ static uint8_t publish[] = { 13, 0x0C, 0xD2, 'R', 'B', 0x1C, 0x1D, 'z', 'e', 'p', 'h', 'y', 'r' }; /* publish ack with topic ID 0x1A1B, msg ID 0x1C1D, return code rejected: not supported */ static uint8_t puback[] = { 7, 0x0D, 0x1A, 0x1B, 0x1C, 0x1D, 0x03 }; /* pubrec */ static uint8_t pubrec[] = { 4, 0x0F, 0xBE, 0xEF }; /* pubrel */ static uint8_t pubrel[] = { 4, 0x10, 0xBE, 0xEF }; /* pubcomp */ static uint8_t pubcomp[] = { 4, 0x0E, 0xBE, 0xEF }; /* subscribe with flags [DUP, QOS0, topic name], message ID 0x1C1D, for topic "/zephyr" */ static uint8_t subscribe[] = { 12, 0x12, 0x80, 0x1C, 0x1D, '/', 'z', 'e', 'p', 'h', 'y', 'r' }; /* subscribe ack with flags [QOS-1], topic ID 0x1A1B, msg ID 0x1909, * return code rejected - congested */ static uint8_t suback[] = { 8, 0x13, 0x60, 0x1A, 0x1B, 0x19, 0x09, 0x01 }; /* unsubscribe with flags [predefined topic ID], message ID 0x1C1D, for topic 0x1234 */ static uint8_t unsubscribe[] = { 7, 0x14, 0x01, 0x1C, 0x1D, 0x12, 0x34 }; /* unsubscribe ack msg ID 0x1337 */ static uint8_t unsuback[] = { 4, 0x15, 0x13, 0x37 }; /* pingreq from client "zephyrclient" */ static uint8_t pingreq[] = { 14, 0x16, 'z', 'e', 'p', 'h', 'y', 'r', 'c', 'l', 'i', 'e', 'n', 't' }; /* pingreq - empty */ static uint8_t pingreq1[] = { 2, 0x16 }; /* pingresp */ static uint8_t pingresp[] = { 2, 0x17 }; /* disconnect by client with duration 10000 */ static uint8_t disconnect[] = { 4, 0x18, 0x27, 0x10 }; /* empty disconnect by GW */ static uint8_t disconnect_gw[] = { 2, 0x18 }; /* willtopicupd with flags [QOS0, retain], topic "/zephyr" */ static uint8_t willtopicupd[] = { 10, 0x1A, 0x10, '/', 'z', 'e', 'p', 'h', 'y', 'r' }; /* willmsgupd with message "mywill" */ static uint8_t willmsgupd[] = { 8, 0x1C, 'm', 'y', 'w', 'i', 'l', 'l' }; /* willtopicresp */ static uint8_t willtopicresp[] = { 3, 0x1B, 0 }; /* willmsgresp */ static uint8_t willmsgresp[] = { 3, 0x1D, 0 }; static struct mqtt_sn_decode_test decode_tests[] = { { MQTT_SN_DECODE_TEST(advertise), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_ADVERTISE, .params.advertise = { .gw_id = 42, .duration = 0xDEAD } } }, { MQTT_SN_DECODE_TEST(gwinfo), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_GWINFO, .params.gwinfo = { .gw_id = 42, .gw_add = { .data = gwinfo+3, .size = 4 } } } }, { MQTT_SN_DECODE_TEST(connack1), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_CONNACK, .params.connack = { .ret_code = MQTT_SN_CODE_ACCEPTED } } }, { MQTT_SN_DECODE_TEST(connack2), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_CONNACK, .params.connack = { .ret_code = MQTT_SN_CODE_REJECTED_TOPIC_ID } } }, { MQTT_SN_DECODE_TEST(willtopicreq), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_WILLTOPICREQ } }, { MQTT_SN_DECODE_TEST(willmsgreq), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_WILLMSGREQ } }, { MQTT_SN_DECODE_TEST(reg), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_REGISTER, .params.reg = { .topic_id = 0x1A1B, .msg_id = 0x1C1D, .topic = { .data = reg + 6, .size = 7 } } } }, { MQTT_SN_DECODE_TEST(regack), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_REGACK, .params.regack = { .topic_id = 0x1A1B, .msg_id = 0x1C1D, .ret_code = MQTT_SN_CODE_ACCEPTED } } }, { MQTT_SN_DECODE_TEST(publish), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PUBLISH, .params.publish = { .dup = true, .qos = MQTT_SN_QOS_2, .retain = true, .topic_type = MQTT_SN_TOPIC_TYPE_SHORT, .topic_id = 0x5242, /* "RB" in hex */ .msg_id = 0x1C1D, .data = { .data = publish + 7, .size = 6 } } } }, { MQTT_SN_DECODE_TEST(puback), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PUBACK, .params.puback = { .topic_id = 0x1A1B, .msg_id = 0x1C1D, .ret_code = MQTT_SN_CODE_REJECTED_NOTSUP } } }, { MQTT_SN_DECODE_TEST(pubrec), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PUBREC, .params.puback = { .msg_id = 0xBEEF, } } }, { MQTT_SN_DECODE_TEST(pubrel), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PUBREL, .params.pubrel = { .msg_id = 0xBEEF, } } }, { MQTT_SN_DECODE_TEST(pubcomp), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PUBCOMP, .params.pubcomp = { .msg_id = 0xBEEF, } } }, { MQTT_SN_DECODE_TEST(suback), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_SUBACK, .params.suback = { .topic_id = 0x1A1B, .msg_id = 0x1909, .qos = MQTT_SN_QOS_M1, .ret_code = MQTT_SN_CODE_REJECTED_CONGESTION } } }, { MQTT_SN_DECODE_TEST(unsuback), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_UNSUBACK, .params.unsuback = { .msg_id = 0x1337, } } }, { MQTT_SN_DECODE_TEST(pingreq1), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PINGREQ, } }, { MQTT_SN_DECODE_TEST(pingresp), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_PINGRESP, } }, { MQTT_SN_DECODE_TEST(disconnect_gw), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_DISCONNECT, } }, { MQTT_SN_DECODE_TEST(willtopicresp), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_WILLTOPICRESP, } }, { MQTT_SN_DECODE_TEST(willmsgresp), .expected = (struct mqtt_sn_param){ .type = MQTT_SN_MSG_TYPE_WILLMSGRESP, } }, }; static ZTEST(mqtt_sn_packet, test_mqtt_packet_decode) { struct net_buf_simple msg; struct mqtt_sn_param param; int err; for (size_t i = 0; i < ARRAY_SIZE(decode_tests); i++) { TC_PRINT("%s - test %zu: %s\n", __func__, i, decode_tests[i].name); LOG_HEXDUMP_DBG(decode_tests[i].data, decode_tests[i].datasz, "Test data"); if (i == 4) { LOG_HEXDUMP_INF(willtopicreq, sizeof(willtopicreq), "willtopicreq"); } memset(¶m, 0, sizeof(param)); net_buf_simple_init_with_data(&msg, decode_tests[i].data, decode_tests[i].datasz); err = mqtt_sn_decode_msg(&msg, ¶m); zassert_equal(err, 0, "Unexpected error %d", err); LOG_HEXDUMP_DBG(¶m, sizeof(param), "Decoded data"); LOG_HEXDUMP_DBG(&decode_tests[i].expected, sizeof(param), "Expected data"); zassert_mem_equal(¶m, &decode_tests[i].expected, sizeof(param), "in test %zu", i); } } struct mqtt_sn_encode_test { const char *name; const uint8_t *expected; size_t expectedsz; struct mqtt_sn_param p; }; #define MQTT_SN_ENCODE_TEST(_name) \ .name = STRINGIFY(_name), .expected = _name, .expectedsz = sizeof(_name) static struct mqtt_sn_encode_test encode_tests[] = { { MQTT_SN_ENCODE_TEST(searchgw), .p = { .type = MQTT_SN_MSG_TYPE_SEARCHGW, .params.searchgw = { .radius = 1 } } }, { MQTT_SN_ENCODE_TEST(gwinfo), .p = { .type = MQTT_SN_MSG_TYPE_GWINFO, .params.gwinfo = { .gw_id = 42, .gw_add = MQTT_SN_DATA_BYTES(127, 0, 0, 1) } } }, { MQTT_SN_ENCODE_TEST(connect), .p = { .type = MQTT_SN_MSG_TYPE_CONNECT, .params.connect = { .will = true, .clean_session = true, .client_id = MQTT_SN_DATA_STRING_LITERAL("zephyrclient"), .duration = 300, } } }, { MQTT_SN_ENCODE_TEST(willtopic), .p = { .type = MQTT_SN_MSG_TYPE_WILLTOPIC, .params.willtopic = { .qos = MQTT_SN_QOS_1, .retain = true, .topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr") } } }, { MQTT_SN_ENCODE_TEST(willmsg), .p = { .type = MQTT_SN_MSG_TYPE_WILLMSG, .params.willmsg = { .msg = MQTT_SN_DATA_STRING_LITERAL("mywill") } } }, { MQTT_SN_ENCODE_TEST(reg_client), .p = { .type = MQTT_SN_MSG_TYPE_REGISTER, .params.reg = { /* * The client must not encode the topic ID - * check this is followed */ .topic_id = 0x1A1B, .msg_id = 0x1C1D, .topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr") } } }, { MQTT_SN_ENCODE_TEST(regack), .p = { .type = MQTT_SN_MSG_TYPE_REGACK, .params.regack = { .topic_id = 0x1A1B, .msg_id = 0x1C1D, .ret_code = MQTT_SN_CODE_ACCEPTED } } }, { MQTT_SN_ENCODE_TEST(publish), .p = { .type = MQTT_SN_MSG_TYPE_PUBLISH, .params.publish = { .dup = true, .qos = MQTT_SN_QOS_2, .retain = true, .topic_id = 0x5242, .topic_type = MQTT_SN_TOPIC_TYPE_SHORT, .msg_id = 0x1C1D, .data = MQTT_SN_DATA_STRING_LITERAL("zephyr") } } }, { MQTT_SN_ENCODE_TEST(puback), .p = { .type = MQTT_SN_MSG_TYPE_PUBACK, .params.puback = { .topic_id = 0x1A1B, .msg_id = 0x1C1D, .ret_code = MQTT_SN_CODE_REJECTED_NOTSUP } } }, { MQTT_SN_ENCODE_TEST(pubrec), .p = { .type = MQTT_SN_MSG_TYPE_PUBREC, .params.pubrec = { .msg_id = 0xBEEF } } }, { MQTT_SN_ENCODE_TEST(pubrel), .p = { .type = MQTT_SN_MSG_TYPE_PUBREL, .params.pubrel = { .msg_id = 0xBEEF } } }, { MQTT_SN_ENCODE_TEST(pubcomp), .p = { .type = MQTT_SN_MSG_TYPE_PUBCOMP, .params.pubcomp = { .msg_id = 0xBEEF } } }, { MQTT_SN_ENCODE_TEST(subscribe), .p = { .type = MQTT_SN_MSG_TYPE_SUBSCRIBE, .params.subscribe = { .dup = true, .qos = MQTT_SN_QOS_0, .msg_id = 0x1C1D, .topic_type = MQTT_SN_FLAGS_TOPICID_TYPE_NORMAL, .topic.topic_name = MQTT_SN_DATA_STRING_LITERAL("/zephyr") } } }, { MQTT_SN_ENCODE_TEST(unsubscribe), .p = { .type = MQTT_SN_MSG_TYPE_UNSUBSCRIBE, .params.unsubscribe = { .msg_id = 0x1C1D, .topic_type = MQTT_SN_FLAGS_TOPICID_TYPE_PREDEF, .topic.topic_id = 0x1234 } } }, { MQTT_SN_ENCODE_TEST(pingreq), .p = { .type = MQTT_SN_MSG_TYPE_PINGREQ, .params.pingreq = { .client_id = MQTT_SN_DATA_STRING_LITERAL("zephyrclient") } } }, { MQTT_SN_ENCODE_TEST(pingresp), .p = { .type = MQTT_SN_MSG_TYPE_PINGRESP } }, { MQTT_SN_ENCODE_TEST(disconnect), .p = { .type = MQTT_SN_MSG_TYPE_DISCONNECT, .params.disconnect = { .duration = 10000 } } }, { MQTT_SN_ENCODE_TEST(willtopicupd), .p = { .type = MQTT_SN_MSG_TYPE_WILLTOPICUPD, .params.willtopicupd = { .qos = MQTT_SN_QOS_0, .retain = true, .topic = MQTT_SN_DATA_STRING_LITERAL("/zephyr") } } }, { MQTT_SN_ENCODE_TEST(willmsgupd), .p = { .type = MQTT_SN_MSG_TYPE_WILLMSGUPD, .params.willmsgupd = { .msg = MQTT_SN_DATA_STRING_LITERAL("mywill") } } } }; static ZTEST(mqtt_sn_packet, test_mqtt_packet_encode) { NET_BUF_SIMPLE_DEFINE(msg, 255); int err; for (size_t i = 0; i < ARRAY_SIZE(encode_tests); i++) { net_buf_simple_reset(&msg); TC_PRINT("%s - test %zu: %s\n", __func__, i, encode_tests[i].name); err = mqtt_sn_encode_msg(&msg, &encode_tests[i].p); zassert_equal(err, 0, "Unexpected error"); LOG_HEXDUMP_DBG(encode_tests[i].expected, encode_tests[i].expectedsz, "Expected data"); LOG_HEXDUMP_DBG(msg.data, msg.len, "Encoded data"); zassert_equal(msg.len, encode_tests[i].expectedsz, "Unexpected data size %zu (%zu)", msg.len, encode_tests[i].expectedsz); zassert_mem_equal(encode_tests[i].expected, msg.data, msg.len, "Bad encoded message"); } } ZTEST_SUITE(mqtt_sn_packet, NULL, NULL, NULL, NULL, NULL);