tests: net: Add mqtt testcases

Add two testcases for mqtt_publisher and mqtt_subscriber.
The combination of the two, covering most of the MQTT APIs.

Change-Id: Ib663ee1f550362ca493501046ddf8508a25ac956
Signed-off-by: Wu Jiequan <jiequanx.wu@intel.com>
This commit is contained in:
Wu Jiequan 2017-03-30 19:46:45 +08:00 committed by Jukka Rissanen
commit 0e3d582623
18 changed files with 1367 additions and 0 deletions

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,3 @@
include $(ZEPHYR_BASE)/tests/Makefile.test
obj-y += main.o test_mqtt_publish.o

View file

@ -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

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
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);
}

View file

@ -0,0 +1,448 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <net/mqtt.h>
#include <ztest.h>
#include <net/net_context.h>
#include <net/nbuf.h>
#include <string.h>
#include <errno.h>
#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);
}

View file

@ -0,0 +1,4 @@
[test]
tags = net mqtt
build_only = true
platform_whitelist = frdm_k64f qemu_x86

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -0,0 +1,24 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
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);
}

View file

@ -0,0 +1,483 @@
/*
* Copyright (c) 2017 Intel Corporation
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ztest.h>
#include <net/mqtt.h>
#include <net/net_context.h>
#include <net/nbuf.h>
#include <misc/printk.h>
#include <string.h>
#include <errno.h>
#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);
}

View file

@ -0,0 +1,4 @@
[test]
tags = net mqtt
build_only = true
platform_whitelist = frdm_k64f qemu_x86