diff --git a/tests/net/mqtt_publisher/Makefile b/tests/net/mqtt_publisher/Makefile new file mode 100644 index 00000000000..b51fac14872 --- /dev/null +++ b/tests/net/mqtt_publisher/Makefile @@ -0,0 +1,8 @@ +BOARD ?= qemu_x86 +CONF_FILE ?= prj_$(BOARD).conf + +include $(ZEPHYR_BASE)/Makefile.test + +ifeq ($(BOARD), qemu_x86) + include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack +endif diff --git a/tests/net/mqtt_publisher/prj_arduino_101.conf b/tests/net/mqtt_publisher/prj_arduino_101.conf new file mode 100644 index 00000000000..0750ad90d22 --- /dev/null +++ b/tests/net/mqtt_publisher/prj_arduino_101.conf @@ -0,0 +1,52 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TCP=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_NET_ARP=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_LOG=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_NBUF_RX_COUNT=16 +CONFIG_NET_NBUF_TX_COUNT=16 +CONFIG_NET_NBUF_RX_DATA_COUNT=16 +CONFIG_NET_NBUF_TX_DATA_COUNT=16 + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3 + +CONFIG_PRINTK=y +#CONFIG_NET_DEBUG_NET_BUF=y + +CONFIG_NET_IPV4=y +# Enable IPv6 support +CONFIG_NET_IPV6=n + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.168.1.10" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_NBUF_DATA_SIZE=256 + +CONFIG_TEST_RANDOM_GENERATOR=y + +# ENC28J60 Ethernet Device +CONFIG_ETH_ENC28J60=y +CONFIG_ETH_ENC28J60_0=y +CONFIG_ETH_ENC28J60_0_SPI_PORT_NAME="SPI_1" +CONFIG_ETH_ENC28J60_0_MAC3=0x2D +CONFIG_ETH_ENC28J60_0_MAC4=0x30 +CONFIG_ETH_ENC28J60_0_MAC5=0x36 + +# Arduino 101 +CONFIG_SPI=y + +CONFIG_ZTEST=y diff --git a/tests/net/mqtt_publisher/prj_frdm_k64f.conf b/tests/net/mqtt_publisher/prj_frdm_k64f.conf new file mode 100644 index 00000000000..119e4219215 --- /dev/null +++ b/tests/net/mqtt_publisher/prj_frdm_k64f.conf @@ -0,0 +1,39 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TCP=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_NET_ARP=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_LOG=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_NBUF_RX_COUNT=16 +CONFIG_NET_NBUF_TX_COUNT=16 +CONFIG_NET_NBUF_RX_DATA_COUNT=16 +CONFIG_NET_NBUF_TX_DATA_COUNT=16 + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3 + +CONFIG_PRINTK=y +#CONFIG_NET_DEBUG_NET_BUF=y + +CONFIG_NET_IPV4=y +# Enable IPv6 support +CONFIG_NET_IPV6=n + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.168.1.10" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_NBUF_DATA_SIZE=256 + +CONFIG_ZTEST=y diff --git a/tests/net/mqtt_publisher/prj_qemu_x86.conf b/tests/net/mqtt_publisher/prj_qemu_x86.conf new file mode 100644 index 00000000000..1ffbd85da2b --- /dev/null +++ b/tests/net/mqtt_publisher/prj_qemu_x86.conf @@ -0,0 +1,40 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TCP=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_LOG=y +CONFIG_NET_SLIP_TAP=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_NBUF_RX_COUNT=16 +CONFIG_NET_NBUF_TX_COUNT=16 +CONFIG_NET_NBUF_RX_DATA_COUNT=16 +CONFIG_NET_NBUF_TX_DATA_COUNT=16 + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2 + +CONFIG_STDOUT_CONSOLE=y + +# Enable IPv6 support +CONFIG_NET_IPV6=n +# Enable IPv4 support +CONFIG_NET_IPV4=y + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_NBUF_DATA_SIZE=256 + +CONFIG_ZTEST=y diff --git a/tests/net/mqtt_publisher/src/Makefile b/tests/net/mqtt_publisher/src/Makefile new file mode 100644 index 00000000000..5c11c3c3dac --- /dev/null +++ b/tests/net/mqtt_publisher/src/Makefile @@ -0,0 +1,3 @@ +include $(ZEPHYR_BASE)/tests/Makefile.test + +obj-y += main.o test_mqtt_publish.o diff --git a/tests/net/mqtt_publisher/src/config.h b/tests/net/mqtt_publisher/src/config.h new file mode 100644 index 00000000000..f9f96c085cf --- /dev/null +++ b/tests/net/mqtt_publisher/src/config.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#ifdef CONFIG_NET_APP_SETTINGS +#ifdef CONFIG_NET_IPV6 +#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV6_ADDR +#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV6_ADDR +#else +#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV4_ADDR +#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV4_ADDR +#endif +#else +#ifdef CONFIG_NET_IPV6 +#define ZEPHYR_ADDR "2001:db8::1" +#define SERVER_ADDR "2001:db8::2" +#else +#define ZEPHYR_ADDR "192.168.1.101" +#define SERVER_ADDR "192.168.1.10" +#endif +#endif + +#define SERVER_PORT 1883 + +#define APP_SLEEP_MSECS 500 +#define APP_TX_RX_TIMEOUT 300 + +#define APP_CONNECT_TRIES 10 + +#define APP_MAX_ITERATIONS 100 + +#define MQTT_CLIENTID "zephyr_publisher" + +/* Set the following to 1 to enable the Bluemix topic format */ +#define APP_BLUEMIX_TOPIC 0 + +#endif diff --git a/tests/net/mqtt_publisher/src/main.c b/tests/net/mqtt_publisher/src/main.c new file mode 100644 index 00000000000..aa142ff621f --- /dev/null +++ b/tests/net/mqtt_publisher/src/main.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +extern void test_mqtt_init(void); +extern void test_mqtt_connect(void); +extern void test_mqtt_pingreq(void); +extern void test_mqtt_publish(void); +extern void test_mqtt_disconnect(void); + +void test_main(void) +{ + ztest_test_suite(mqtt_test, + ztest_unit_test(test_mqtt_init), + ztest_unit_test(test_mqtt_connect), + ztest_unit_test(test_mqtt_pingreq), + ztest_unit_test(test_mqtt_publish), + ztest_unit_test(test_mqtt_disconnect)); + ztest_run_test_suite(mqtt_test); +} diff --git a/tests/net/mqtt_publisher/src/test_mqtt_publish.c b/tests/net/mqtt_publisher/src/test_mqtt_publish.c new file mode 100644 index 00000000000..e245ee70944 --- /dev/null +++ b/tests/net/mqtt_publisher/src/test_mqtt_publish.c @@ -0,0 +1,448 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#include +#include + +#include "config.h" + +/* Container for some structures used by the MQTT publisher app. */ +struct mqtt_client_ctx { + /** + * The connect message structure is only used during the connect + * stage. Developers must set some msg properties before calling the + * mqtt_tx_connect routine. See below. + */ + struct mqtt_connect_msg connect_msg; + /** + * This is the message that will be received by the server + * (MQTT broker). + */ + struct mqtt_publish_msg pub_msg; + + /** + * This is the MQTT application context variable. + */ + struct mqtt_ctx mqtt_ctx; + + /** + * This variable will be passed to the connect callback, declared inside + * the mqtt context struct. If not used, it could be set to NULL. + */ + void *connect_data; + + /** + * This variable will be passed to the disconnect callback, declared + * inside the mqtt context struct. If not used, it could be set to NULL. + */ + void *disconnect_data; + + /** + * This variable will be passed to the publish_tx callback, declared + * inside the mqtt context struct. If not used, it could be set to NULL. + */ + void *publish_data; +}; + +/* This is mqtt payload message. */ +char payload[] = "DOORS:OPEN_QoSx"; + +/* This is the network context structure. */ +static struct net_context *net_ctx; + +/* The mqtt client struct */ +static struct mqtt_client_ctx client_ctx; + +/* This routine sets some basic properties for the network context variable */ +static int network_setup(struct net_context **net_ctx, const char *local_addr, + const char *server_addr, uint16_t server_port); + +/* The signature of this routine must match the connect callback declared at + * the mqtt.h header. + */ +static void connect_cb(struct mqtt_ctx *mqtt_ctx) +{ + struct mqtt_client_ctx *client_ctx; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + TC_PRINT("[%s:%d]", __func__, __LINE__); + + if (client_ctx->connect_data) { + TC_PRINT(" user_data: %s", + (const char *)client_ctx->connect_data); + } + + TC_PRINT("\n"); +} + +/* The signature of this routine must match the disconnect callback declared at + * the mqtt.h header. + */ +static void disconnect_cb(struct mqtt_ctx *mqtt_ctx) +{ + struct mqtt_client_ctx *client_ctx; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + TC_PRINT("[%s:%d]", __func__, __LINE__); + + if (client_ctx->disconnect_data) { + TC_PRINT(" user_data: %s", + (const char *)client_ctx->disconnect_data); + } + + TC_PRINT("\n"); +} + +/** + * The signature of this routine must match the publish_tx callback declared at + * the mqtt.h header. + * + * NOTE: we have two callbacks for MQTT Publish related stuff: + * - publish_tx, for publishers + * - publish_rx, for subscribers + * + * Applications must keep a "message database" with pkt_id's. So far, this is + * not implemented here. For example, if we receive a PUBREC message with an + * unknown pkt_id, this routine must return an error, for example -EINVAL or + * any negative value. + */ +static int publish_cb(struct mqtt_ctx *mqtt_ctx, uint16_t pkt_id, + enum mqtt_packet type) +{ + struct mqtt_client_ctx *client_ctx; + const char *str; + int rc = 0; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + switch (type) { + case MQTT_PUBACK: + str = "MQTT_PUBACK"; + break; + case MQTT_PUBCOMP: + str = "MQTT_PUBCOMP"; + break; + case MQTT_PUBREC: + str = "MQTT_PUBREC"; + break; + default: + rc = -EINVAL; + str = "Invalid MQTT packet"; + } + + TC_PRINT("[%s:%d] <%s> packet id: %u", __func__, __LINE__, str, pkt_id); + + if (client_ctx->publish_data) { + TC_PRINT(", user_data: %s", + (const char *)client_ctx->publish_data); + } + + TC_PRINT("\n"); + + return rc; +} + +/** + * The signature of this routine must match the malformed callback declared at + * the mqtt.h header. + */ +static void malformed_cb(struct mqtt_ctx *mqtt_ctx, uint16_t pkt_type) +{ + TC_PRINT("[%s:%d] pkt_type: %u\n", __func__, __LINE__, pkt_type); +} + +static char *get_mqtt_payload(enum mqtt_qos qos) +{ + payload[strlen(payload) - 1] = '0' + qos; + + return payload; +} + +static char *get_mqtt_topic(void) +{ + return "sensors"; +} + +static void prepare_mqtt_publish_msg(struct mqtt_publish_msg *pub_msg, + enum mqtt_qos qos) +{ + /* MQTT message payload may be anything, we we use C strings */ + pub_msg->msg = (uint8_t *)get_mqtt_payload(qos); + /* Payload's length */ + pub_msg->msg_len = (uint16_t)strlen((char *)client_ctx.pub_msg.msg); + /* MQTT Quality of Service */ + pub_msg->qos = qos; + /* Message's topic */ + pub_msg->topic = get_mqtt_topic(); + pub_msg->topic_len = strlen(client_ctx.pub_msg.topic); + /* Packet Identifier, always use different values */ + pub_msg->pkt_id = sys_rand32_get(); +} + +#define RC_STR(rc) ((rc) == 0 ? "OK" : "ERROR") + +#define PRINT_RESULT(func, rc) \ + TC_PRINT("[%s:%d] %s: %d <%s>\n", __func__, __LINE__, \ + (func), rc, RC_STR(rc)) + +/* In this routine we block until the connected variable is 1 */ +static int try_to_connect(struct mqtt_client_ctx *client_ctx) +{ + int i = 0; + + while (i++ < APP_CONNECT_TRIES && !client_ctx->mqtt_ctx.connected) { + int rc; + + rc = mqtt_tx_connect(&client_ctx->mqtt_ctx, + &client_ctx->connect_msg); + k_sleep(APP_SLEEP_MSECS); + if (rc != 0) { + continue; + } + } + + if (client_ctx->mqtt_ctx.connected) { + return TC_PASS; + } + + return TC_FAIL; +} + +static int init_network(void) +{ + int rc; + + /* The net_ctx variable must be ready BEFORE passing it to the MQTT API. + */ + rc = network_setup(&net_ctx, ZEPHYR_ADDR, SERVER_ADDR, SERVER_PORT); + if (rc != 0) { + goto exit_app; + } + + /* Set everything to 0 and later just assign the required fields. */ + memset(&client_ctx, 0x00, sizeof(client_ctx)); + + /* The network context is the only field that must be set BEFORE + * calling the mqtt_init routine. + */ + client_ctx.mqtt_ctx.net_ctx = net_ctx; + + /* connect, disconnect and malformed may be set to NULL */ + client_ctx.mqtt_ctx.connect = connect_cb; + + client_ctx.mqtt_ctx.disconnect = disconnect_cb; + client_ctx.mqtt_ctx.malformed = malformed_cb; + + client_ctx.mqtt_ctx.net_timeout = APP_TX_RX_TIMEOUT; + + /* Publisher apps TX the MQTT PUBLISH msg */ + client_ctx.mqtt_ctx.publish_tx = publish_cb; + + rc = mqtt_init(&client_ctx.mqtt_ctx, MQTT_APP_PUBLISHER); + if (rc != 0) { + goto exit_app; + } + + /* The connect message will be sent to the MQTT server (broker). + * If clean_session here is 0, the mqtt_ctx clean_session variable + * will be set to 0 also. Please don't do that, set always to 1. + * Clean session = 0 is not yet supported. + */ + client_ctx.connect_msg.client_id = MQTT_CLIENTID; + client_ctx.connect_msg.client_id_len = strlen(MQTT_CLIENTID); + client_ctx.connect_msg.clean_session = 1; + + client_ctx.connect_data = "CONNECTED"; + client_ctx.disconnect_data = "DISCONNECTED"; + client_ctx.publish_data = "PUBLISH"; + + return TC_PASS; + +exit_app: + net_context_put(net_ctx); + + return TC_FAIL; +} + +static int test_connect(void) +{ + int rc; + + rc = try_to_connect(&client_ctx); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; + +} + +static int test_pingreq(void) +{ + int rc; + + rc = mqtt_tx_pingreq(&client_ctx.mqtt_ctx); + k_sleep(APP_SLEEP_MSECS); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int test_publish(enum mqtt_qos qos) +{ + int rc; + + prepare_mqtt_publish_msg(&client_ctx.pub_msg, qos); + rc = mqtt_tx_publish(&client_ctx.mqtt_ctx, &client_ctx.pub_msg); + k_sleep(APP_SLEEP_MSECS); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int test_disconnect(void) +{ + int rc; + + rc = mqtt_tx_disconnect(&client_ctx.mqtt_ctx); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int set_addr(struct sockaddr *sock_addr, const char *addr, uint16_t port) +{ + void *ptr; + int rc; + +#ifdef CONFIG_NET_IPV6 + net_sin6(sock_addr)->sin6_port = htons(port); + sock_addr->family = AF_INET6; + ptr = &(net_sin6(sock_addr)->sin6_addr); + rc = net_addr_pton(AF_INET6, addr, ptr); +#else + net_sin(sock_addr)->sin_port = htons(port); + sock_addr->family = AF_INET; + ptr = &(net_sin(sock_addr)->sin_addr); + rc = net_addr_pton(AF_INET, addr, ptr); +#endif + + if (rc) { + TC_PRINT("Invalid IP address: %s\n", addr); + } + + return rc; +} + +static int network_setup(struct net_context **net_ctx, const char *local_addr, + const char *server_addr, uint16_t server_port) +{ +#ifdef CONFIG_NET_IPV6 + socklen_t addr_len = sizeof(struct sockaddr_in6); + sa_family_t family = AF_INET6; + +#else + socklen_t addr_len = sizeof(struct sockaddr_in); + sa_family_t family = AF_INET; +#endif + struct sockaddr server_sock, local_sock; + void *p; + int rc; + + rc = set_addr(&local_sock, local_addr, 0); + if (rc) { + TC_PRINT("set_addr (local) error\n"); + return TC_FAIL; + } + +#ifdef CONFIG_NET_IPV6 + p = net_if_ipv6_addr_add(net_if_get_default(), + &net_sin6(&local_sock)->sin6_addr, + NET_ADDR_MANUAL, 0); +#else + p = net_if_ipv4_addr_add(net_if_get_default(), + &net_sin(&local_sock)->sin_addr, + NET_ADDR_MANUAL, 0); +#endif + + if (!p) { + return TC_FAIL; + } + + rc = net_context_get(family, SOCK_STREAM, IPPROTO_TCP, net_ctx); + if (rc) { + TC_PRINT("net_context_get error\n"); + return TC_FAIL; + } + + rc = net_context_bind(*net_ctx, &local_sock, addr_len); + if (rc) { + TC_PRINT("net_context_bind error\n"); + goto lb_exit; + } + + rc = set_addr(&server_sock, server_addr, server_port); + if (rc) { + TC_PRINT("set_addr (server) error\n"); + goto lb_exit; + } + + rc = net_context_connect(*net_ctx, &server_sock, addr_len, NULL, + APP_SLEEP_MSECS, NULL); + if (rc) { + TC_PRINT("net_context_connect error\n" + "Is the server (broker) up and running?\n"); + goto lb_exit; + } + + return TC_PASS; + +lb_exit: + net_context_put(*net_ctx); + + return TC_FAIL; +} + +void test_mqtt_init(void) +{ + assert_true(init_network() == TC_PASS, NULL); +} + +void test_mqtt_connect(void) +{ + assert_true(test_connect() == TC_PASS, NULL); +} + +void test_mqtt_pingreq(void) +{ + assert_true(test_pingreq() == TC_PASS, NULL); +} + +void test_mqtt_publish(void) +{ + assert_true(test_publish(MQTT_QoS0) == TC_PASS, NULL); + assert_true(test_publish(MQTT_QoS1) == TC_PASS, NULL); + assert_true(test_publish(MQTT_QoS2) == TC_PASS, NULL); +} + +void test_mqtt_disconnect(void) +{ + assert_true(test_disconnect() == TC_PASS, NULL); +} diff --git a/tests/net/mqtt_publisher/testcase.ini b/tests/net/mqtt_publisher/testcase.ini new file mode 100644 index 00000000000..24049837541 --- /dev/null +++ b/tests/net/mqtt_publisher/testcase.ini @@ -0,0 +1,4 @@ +[test] +tags = net mqtt +build_only = true +platform_whitelist = frdm_k64f qemu_x86 diff --git a/tests/net/mqtt_subscriber/Makefile b/tests/net/mqtt_subscriber/Makefile new file mode 100644 index 00000000000..2bfab2e2dba --- /dev/null +++ b/tests/net/mqtt_subscriber/Makefile @@ -0,0 +1,14 @@ +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# + +BOARD ?= qemu_x86 +CONF_FILE ?= prj_$(BOARD).conf + +include $(ZEPHYR_BASE)/Makefile.test + +ifeq ($(BOARD), qemu_x86) + include $(ZEPHYR_BASE)/samples/net/common/Makefile.ipstack +endif diff --git a/tests/net/mqtt_subscriber/prj_arduino_101.conf b/tests/net/mqtt_subscriber/prj_arduino_101.conf new file mode 100644 index 00000000000..0750ad90d22 --- /dev/null +++ b/tests/net/mqtt_subscriber/prj_arduino_101.conf @@ -0,0 +1,52 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TCP=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_NET_ARP=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_LOG=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_NBUF_RX_COUNT=16 +CONFIG_NET_NBUF_TX_COUNT=16 +CONFIG_NET_NBUF_RX_DATA_COUNT=16 +CONFIG_NET_NBUF_TX_DATA_COUNT=16 + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3 + +CONFIG_PRINTK=y +#CONFIG_NET_DEBUG_NET_BUF=y + +CONFIG_NET_IPV4=y +# Enable IPv6 support +CONFIG_NET_IPV6=n + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.168.1.10" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_NBUF_DATA_SIZE=256 + +CONFIG_TEST_RANDOM_GENERATOR=y + +# ENC28J60 Ethernet Device +CONFIG_ETH_ENC28J60=y +CONFIG_ETH_ENC28J60_0=y +CONFIG_ETH_ENC28J60_0_SPI_PORT_NAME="SPI_1" +CONFIG_ETH_ENC28J60_0_MAC3=0x2D +CONFIG_ETH_ENC28J60_0_MAC4=0x30 +CONFIG_ETH_ENC28J60_0_MAC5=0x36 + +# Arduino 101 +CONFIG_SPI=y + +CONFIG_ZTEST=y diff --git a/tests/net/mqtt_subscriber/prj_frdm_k64f.conf b/tests/net/mqtt_subscriber/prj_frdm_k64f.conf new file mode 100644 index 00000000000..119e4219215 --- /dev/null +++ b/tests/net/mqtt_subscriber/prj_frdm_k64f.conf @@ -0,0 +1,39 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TCP=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_NET_ARP=y +CONFIG_NET_L2_ETHERNET=y +CONFIG_NET_LOG=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_NBUF_RX_COUNT=16 +CONFIG_NET_NBUF_TX_COUNT=16 +CONFIG_NET_NBUF_RX_DATA_COUNT=16 +CONFIG_NET_NBUF_TX_DATA_COUNT=16 + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IFACE_UNICAST_IPV4_ADDR_COUNT=3 + +CONFIG_PRINTK=y +#CONFIG_NET_DEBUG_NET_BUF=y + +CONFIG_NET_IPV4=y +# Enable IPv6 support +CONFIG_NET_IPV6=n + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_APP_MY_IPV4_ADDR="192.168.1.101" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.168.1.10" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_NBUF_DATA_SIZE=256 + +CONFIG_ZTEST=y diff --git a/tests/net/mqtt_subscriber/prj_qemu_x86.conf b/tests/net/mqtt_subscriber/prj_qemu_x86.conf new file mode 100644 index 00000000000..1ffbd85da2b --- /dev/null +++ b/tests/net/mqtt_subscriber/prj_qemu_x86.conf @@ -0,0 +1,40 @@ +CONFIG_NETWORKING=y +CONFIG_NET_TCP=y +CONFIG_RANDOM_GENERATOR=y +CONFIG_TEST_RANDOM_GENERATOR=y +CONFIG_NET_LOG=y +CONFIG_NET_SLIP_TAP=y +CONFIG_INIT_STACKS=y + +CONFIG_NET_NBUF_RX_COUNT=16 +CONFIG_NET_NBUF_TX_COUNT=16 +CONFIG_NET_NBUF_RX_DATA_COUNT=16 +CONFIG_NET_NBUF_TX_DATA_COUNT=16 + +CONFIG_NET_IPV6_RA_RDNSS=y +CONFIG_NET_IF_UNICAST_IPV6_ADDR_COUNT=3 +CONFIG_NET_IF_MCAST_IPV6_ADDR_COUNT=2 + +CONFIG_STDOUT_CONSOLE=y + +# Enable IPv6 support +CONFIG_NET_IPV6=n +# Enable IPv4 support +CONFIG_NET_IPV4=y + +# Enable the MQTT Lib +CONFIG_MQTT_LIB=y + +CONFIG_NET_APP_SETTINGS=y +CONFIG_NET_APP_MY_IPV6_ADDR="2001:db8::1" +CONFIG_NET_APP_PEER_IPV6_ADDR="2001:db8::2" + +CONFIG_NET_APP_MY_IPV4_ADDR="192.0.2.1" +CONFIG_NET_APP_PEER_IPV4_ADDR="192.0.2.2" + +CONFIG_MAIN_STACK_SIZE=2048 + +# For IPv6 +CONFIG_NET_NBUF_DATA_SIZE=256 + +CONFIG_ZTEST=y diff --git a/tests/net/mqtt_subscriber/src/Makefile b/tests/net/mqtt_subscriber/src/Makefile new file mode 100644 index 00000000000..64aa88800c6 --- /dev/null +++ b/tests/net/mqtt_subscriber/src/Makefile @@ -0,0 +1,9 @@ +# +# Copyright (c) 2017 Intel Corporation +# +# SPDX-License-Identifier: Apache-2.0 +# +# +include $(ZEPHYR_BASE)/tests/Makefile.test +obj-y += main.o test_mqtt_subscribe.o + diff --git a/tests/net/mqtt_subscriber/src/config.h b/tests/net/mqtt_subscriber/src/config.h new file mode 100644 index 00000000000..f9f96c085cf --- /dev/null +++ b/tests/net/mqtt_subscriber/src/config.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#ifndef __CONFIG_H__ +#define __CONFIG_H__ + +#ifdef CONFIG_NET_APP_SETTINGS +#ifdef CONFIG_NET_IPV6 +#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV6_ADDR +#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV6_ADDR +#else +#define ZEPHYR_ADDR CONFIG_NET_APP_MY_IPV4_ADDR +#define SERVER_ADDR CONFIG_NET_APP_PEER_IPV4_ADDR +#endif +#else +#ifdef CONFIG_NET_IPV6 +#define ZEPHYR_ADDR "2001:db8::1" +#define SERVER_ADDR "2001:db8::2" +#else +#define ZEPHYR_ADDR "192.168.1.101" +#define SERVER_ADDR "192.168.1.10" +#endif +#endif + +#define SERVER_PORT 1883 + +#define APP_SLEEP_MSECS 500 +#define APP_TX_RX_TIMEOUT 300 + +#define APP_CONNECT_TRIES 10 + +#define APP_MAX_ITERATIONS 100 + +#define MQTT_CLIENTID "zephyr_publisher" + +/* Set the following to 1 to enable the Bluemix topic format */ +#define APP_BLUEMIX_TOPIC 0 + +#endif diff --git a/tests/net/mqtt_subscriber/src/main.c b/tests/net/mqtt_subscriber/src/main.c new file mode 100644 index 00000000000..25a3e87ae1e --- /dev/null +++ b/tests/net/mqtt_subscriber/src/main.c @@ -0,0 +1,24 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +extern void test_mqtt_init(void); +extern void test_mqtt_connect(void); +extern void test_mqtt_subscribe(void); +extern void test_mqtt_unsubscribe(void); +extern void test_mqtt_disconnect(void); + +void test_main(void) +{ + ztest_test_suite(mqtt_test, + ztest_unit_test(test_mqtt_init), + ztest_unit_test(test_mqtt_connect), + ztest_unit_test(test_mqtt_subscribe), + ztest_unit_test(test_mqtt_unsubscribe), + ztest_unit_test(test_mqtt_disconnect)); + ztest_run_test_suite(mqtt_test); +} diff --git a/tests/net/mqtt_subscriber/src/test_mqtt_subscribe.c b/tests/net/mqtt_subscriber/src/test_mqtt_subscribe.c new file mode 100644 index 00000000000..7e9e15e800b --- /dev/null +++ b/tests/net/mqtt_subscriber/src/test_mqtt_subscribe.c @@ -0,0 +1,483 @@ +/* + * Copyright (c) 2017 Intel Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include + +#include +#include + +#include +#include +#include + +#include "config.h" + +/* Container for some structures used by the MQTT subscriber app. */ +struct mqtt_client_ctx { + /** + * The connect message structure is only used during the connect + * stage. Developers must set some msg properties before calling the + * mqtt_tx_connect routine. See below. + */ + struct mqtt_connect_msg connect_msg; + /** + * This is the message that will be received by the server + * (MQTT broker). + */ + struct mqtt_publish_msg pub_msg; + + /** + * This is the MQTT application context variable. + */ + struct mqtt_ctx mqtt_ctx; + + /** + * This variable will be passed to the connect callback, declared inside + * the mqtt context struct. If not used, it could be set to NULL. + */ + void *connect_data; + + /** + * This variable will be passed to the disconnect callback, declared + * inside the mqtt context struct. If not used, it could be set to NULL. + */ + void *disconnect_data; + + /** + * This variable will be passed to the subscribe_tx callback, declared + * inside the mqtt context struct. If not used, it could be set to NULL. + */ + void *subscribe_data; + + /** + * This variable will be passed to the unsubscribe_tx callback, declared + * inside the mqtt context struct. If not used, it could be set to NULL. + */ + void *unsubscribe_data; +}; + +/* This is the network context structure. */ +static struct net_context *net_ctx; + +/* The mqtt client struct */ +static struct mqtt_client_ctx client_ctx; + +/* This routine sets some basic properties for the network context variable */ +static int network_setup(struct net_context **net_ctx, const char *local_addr, + const char *server_addr, uint16_t server_port); + +/* The signature of this routine must match the connect callback declared at + * the mqtt.h header. + */ +static void connect_cb(struct mqtt_ctx *mqtt_ctx) +{ + struct mqtt_client_ctx *client_ctx; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + printk("[%s:%d]", __func__, __LINE__); + + if (client_ctx->connect_data) { + printk(" user_data: %s", + (const char *)client_ctx->connect_data); + } + + printk("\n"); +} + +/* The signature of this routine must match the disconnect callback declared at + * the mqtt.h header. + */ +static void disconnect_cb(struct mqtt_ctx *mqtt_ctx) +{ + struct mqtt_client_ctx *client_ctx; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + printk("[%s:%d]", __func__, __LINE__); + + if (client_ctx->disconnect_data) { + printk(" user_data: %s", + (const char *)client_ctx->disconnect_data); + } + + printk("\n"); +} + +/** + * The signature of this routine must match the publish_rx callback declared at + * the mqtt.h header. + * + * NOTE: we have two callbacks for MQTT Publish related stuff: + * - publish_tx, for publishers + * - publish_rx, for subscribers + * + * Applications must keep a "message database" with pkt_id's. So far, this is + * not implemented here. For example, if we receive a PUBREC message with an + * unknown pkt_id, this routine must return an error, for example -EINVAL or + * any negative value. + */ +static int publish_rx_cb(struct mqtt_ctx *mqtt_ctx, struct mqtt_publish_msg + *msg, uint16_t pkt_id, enum mqtt_packet type) +{ + struct mqtt_client_ctx *client_ctx; + const char *str; + int rc = 0; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + switch (type) { + case MQTT_PUBLISH: + str = "MQTT_PUBLISH"; + printk("[%s:%d] <%s> msg: %s", __func__, __LINE__, + str, msg->msg); + break; + case MQTT_PUBREL: + str = "MQTT_PUBREL"; + printk("[%s:%d] <%s> packet id: %u", __func__, __LINE__, + str, pkt_id); + return 0; + default: + rc = -EINVAL; + str = "Invalid MQTT packet"; + } + + if (client_ctx->subscribe_data) { + printk(", user_data: %s", + (const char *)client_ctx->subscribe_data); + } + + printk("\n"); + + return rc; +} + +/** + * The signature of this routine must match the subscribe callback declared at + * the mqtt.h header. + */ +static int subscriber_cb(struct mqtt_ctx *mqtt_ctx, uint16_t pkt_id, + uint8_t items, enum mqtt_qos qos[]) +{ + struct mqtt_client_ctx *client_ctx; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + printk("[%s:%d] items: %d packet id: %u", __func__, __LINE__, + items, pkt_id); + + if (client_ctx->subscribe_data) { + printk(" user_data: %s", + (const char *)client_ctx->subscribe_data); + } + + printk("\n"); + + return 0; +} + +/** + * The signature of this routine must match the unsubscribe callback declared at + * the mqtt.h header. + */ +static int unsubscribe_cb(struct mqtt_ctx *mqtt_ctx, uint16_t pkt_id) +{ + struct mqtt_client_ctx *client_ctx; + + client_ctx = CONTAINER_OF(mqtt_ctx, struct mqtt_client_ctx, mqtt_ctx); + + printk("[%s:%d] packet id: %u", __func__, __LINE__, pkt_id); + + if (client_ctx->unsubscribe_data) { + printk(" user_data: %s", + (const char *)client_ctx->unsubscribe_data); + } + + printk("\n"); + + return 0; +} + +/** + * The signature of this routine must match the malformed callback declared at + * the mqtt.h header. + */ +static void malformed_cb(struct mqtt_ctx *mqtt_ctx, uint16_t pkt_type) +{ + printk("[%s:%d] pkt_type: %u\n", __func__, __LINE__, pkt_type); +} + +static char *get_mqtt_topic(void) +{ + return "sensors"; +} + +#define RC_STR(rc) ((rc) == 0 ? "OK" : "ERROR") + +#define PRINT_RESULT(func, rc) \ + printk("[%s:%d] %s: %d <%s>\n", __func__, __LINE__, \ + (func), rc, RC_STR(rc)) + +/* In this routine we block until the connected variable is 1 */ +static int try_to_connect(struct mqtt_client_ctx *client_ctx) +{ + int i = 0; + + while (i++ < APP_CONNECT_TRIES && !client_ctx->mqtt_ctx.connected) { + int rc; + + rc = mqtt_tx_connect(&client_ctx->mqtt_ctx, + &client_ctx->connect_msg); + k_sleep(APP_SLEEP_MSECS); + if (rc != 0) { + continue; + } + } + + if (client_ctx->mqtt_ctx.connected) { + return TC_PASS; + } + + return TC_FAIL; +} + +static int init_network(void) +{ + int rc; + + /* The net_ctx variable must be ready BEFORE passing it to the MQTT API. + */ + rc = network_setup(&net_ctx, ZEPHYR_ADDR, SERVER_ADDR, SERVER_PORT); + if (rc != 0) { + goto exit_app; + } + + /* Set everything to 0 and later just assign the required fields. */ + memset(&client_ctx, 0x00, sizeof(client_ctx)); + + /* The network context is the only field that must be set BEFORE + * calling the mqtt_init routine. + */ + client_ctx.mqtt_ctx.net_ctx = net_ctx; + + /* connect, disconnect and malformed may be set to NULL */ + client_ctx.mqtt_ctx.connect = connect_cb; + + client_ctx.mqtt_ctx.disconnect = disconnect_cb; + + client_ctx.mqtt_ctx.malformed = malformed_cb; + + client_ctx.mqtt_ctx.subscribe = subscriber_cb; + + client_ctx.mqtt_ctx.unsubscribe = unsubscribe_cb; + + client_ctx.mqtt_ctx.net_timeout = APP_TX_RX_TIMEOUT; + + /* Publisher apps TX the MQTT PUBLISH msg */ + client_ctx.mqtt_ctx.publish_rx = publish_rx_cb; + + rc = mqtt_init(&client_ctx.mqtt_ctx, MQTT_APP_SUBSCRIBER); + if (rc != 0) { + goto exit_app; + } + + /* The connect message will be sent to the MQTT server (broker). + * If clean_session here is 0, the mqtt_ctx clean_session variable + * will be set to 0 also. Please don't do that, set always to 1. + * Clean session = 0 is not yet supported. + */ + client_ctx.connect_msg.client_id = MQTT_CLIENTID; + client_ctx.connect_msg.client_id_len = strlen(MQTT_CLIENTID); + client_ctx.connect_msg.clean_session = 1; + + client_ctx.connect_data = "CONNECTED"; + client_ctx.disconnect_data = "DISCONNECTED"; + client_ctx.subscribe_data = "SUBSCRIBE"; + client_ctx.unsubscribe_data = "UNSUBSCRIBE"; + + return TC_PASS; + +exit_app: + net_context_put(net_ctx); + + return TC_FAIL; +} + +static int test_connect(void) +{ + int rc; + + rc = try_to_connect(&client_ctx); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int test_subscribe(void) +{ + int rc; + const char *topic_sub = get_mqtt_topic(); + uint16_t pkt_id_sub = sys_rand32_get(); + static enum mqtt_qos mqtt_qos_sub[1]; + + rc = mqtt_tx_subscribe(&client_ctx.mqtt_ctx, pkt_id_sub, 1, + &topic_sub, mqtt_qos_sub); + k_sleep(APP_SLEEP_MSECS); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int test_unsubscribe(void) +{ + int rc; + const char *topic_sub = get_mqtt_topic(); + uint16_t pkt_id_unsub = sys_rand32_get(); + + rc = mqtt_tx_unsubscribe(&client_ctx.mqtt_ctx, pkt_id_unsub, + 1, &topic_sub); + k_sleep(APP_SLEEP_MSECS); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int test_disconnect(void) +{ + int rc; + + rc = mqtt_tx_disconnect(&client_ctx.mqtt_ctx); + if (rc != 0) { + return TC_FAIL; + } + + return TC_PASS; +} + +static int set_addr(struct sockaddr *sock_addr, const char *addr, uint16_t port) +{ + void *ptr; + int rc; + +#ifdef CONFIG_NET_IPV6 + net_sin6(sock_addr)->sin6_port = htons(port); + sock_addr->family = AF_INET6; + ptr = &(net_sin6(sock_addr)->sin6_addr); + rc = net_addr_pton(AF_INET6, addr, ptr); +#else + net_sin(sock_addr)->sin_port = htons(port); + sock_addr->family = AF_INET; + ptr = &(net_sin(sock_addr)->sin_addr); + rc = net_addr_pton(AF_INET, addr, ptr); +#endif + + if (rc) { + printk("Invalid IP address: %s\n", addr); + } + + return rc; +} + +static int network_setup(struct net_context **net_ctx, const char *local_addr, + const char *server_addr, uint16_t server_port) +{ +#ifdef CONFIG_NET_IPV6 + socklen_t addr_len = sizeof(struct sockaddr_in6); + sa_family_t family = AF_INET6; + +#else + socklen_t addr_len = sizeof(struct sockaddr_in); + sa_family_t family = AF_INET; +#endif + struct sockaddr server_sock, local_sock; + void *p; + int rc; + + rc = set_addr(&local_sock, local_addr, 0); + if (rc) { + printk("set_addr (local) error\n"); + return rc; + } + +#ifdef CONFIG_NET_IPV6 + p = net_if_ipv6_addr_add(net_if_get_default(), + &net_sin6(&local_sock)->sin6_addr, + NET_ADDR_MANUAL, 0); +#else + p = net_if_ipv4_addr_add(net_if_get_default(), + &net_sin(&local_sock)->sin_addr, + NET_ADDR_MANUAL, 0); +#endif + + if (!p) { + return -EINVAL; + } + + rc = net_context_get(family, SOCK_STREAM, IPPROTO_TCP, net_ctx); + if (rc) { + printk("net_context_get error\n"); + return rc; + } + + rc = net_context_bind(*net_ctx, &local_sock, addr_len); + if (rc) { + printk("net_context_bind error\n"); + goto lb_exit; + } + + rc = set_addr(&server_sock, server_addr, server_port); + if (rc) { + printk("set_addr (server) error\n"); + goto lb_exit; + } + + rc = net_context_connect(*net_ctx, &server_sock, addr_len, NULL, + APP_SLEEP_MSECS, NULL); + if (rc) { + printk("net_context_connect error\n" + "Is the server (broker) up and running?\n"); + goto lb_exit; + } + + return 0; + +lb_exit: + net_context_put(*net_ctx); + + return rc; +} + +void test_mqtt_init(void) +{ + assert_true(init_network() == TC_PASS, NULL); +} + +void test_mqtt_connect(void) +{ + assert_true(test_connect() == TC_PASS, NULL); +} + +void test_mqtt_subscribe(void) +{ + assert_true(test_subscribe() == TC_PASS, NULL); +} + +void test_mqtt_unsubscribe(void) +{ + assert_true(test_unsubscribe() == TC_PASS, NULL); +} + +void test_mqtt_disconnect(void) +{ + assert_true(test_disconnect() == TC_PASS, NULL); +} diff --git a/tests/net/mqtt_subscriber/testcase.ini b/tests/net/mqtt_subscriber/testcase.ini new file mode 100644 index 00000000000..24049837541 --- /dev/null +++ b/tests/net/mqtt_subscriber/testcase.ini @@ -0,0 +1,4 @@ +[test] +tags = net mqtt +build_only = true +platform_whitelist = frdm_k64f qemu_x86