2021-03-19 20:41:23

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ v4 0/2] Framework for non-interactive mesh test

v4: Use tester frameworks from ELL library

**********
v3: Fixed copyright style in mesh-io-unit.h

**********
v2: Fixed errors reported by bluez-bot.
Regarding the warnings for the "__attribute__((packed))":
keeping this style to be consistent with the rest of BLueZ codebase

**********
This patch set introduces a framework for non-interactive testing of
mesh daemon functionality and consists of the three components:

1. A version of src/shared/tester that uses ELL primitives:
everything mesh-related uses ELL.

2. New type of mesh IO used only for testing.

3. The mesh-cfgtest tool that excercises a number of mesh D-Bus
API calls. Currently, the tool covers limited number of initial
test cases. The extended coverage will be provided after this
patch set is accepted.

Brian Gix (1):
mesh: Add unit test IO

Inga Stotland (1):
tools/mesh-cfgtest: Non-iteractive test for mesh

Makefile.am | 14 +-
Makefile.mesh | 2 +
Makefile.tools | 6 +
mesh/main.c | 51 +-
mesh/mesh-io-unit.c | 533 +++++++++++++++
mesh/mesh-io-unit.h | 11 +
mesh/mesh-io.c | 9 +-
mesh/mesh-io.h | 3 +-
tools/mesh-cfgtest.c | 1458 ++++++++++++++++++++++++++++++++++++++++++
9 files changed, 2065 insertions(+), 22 deletions(-)
create mode 100644 mesh/mesh-io-unit.c
create mode 100644 mesh/mesh-io-unit.h
create mode 100644 tools/mesh-cfgtest.c

--
2.26.2


2021-03-19 20:41:23

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ v4 1/2] mesh: Add unit test IO

From: Brian Gix <[email protected]>

This adds a new type of mesh IO that is used for non-interactive testing.
The new io option can be specified on command line as:
--io unit:<socket_name>

When the bluetooth-meshd daemon starts with the "unit" IO type,
the daemon opens a socket (fd to open is provided after "unit:"
in <socket_name>). The communication with the daemon is done either
through the loop-back using mesh DBus-based APIs or the specified
named socket.
---
Makefile.mesh | 2 +
mesh/main.c | 41 +++-
mesh/mesh-io-unit.c | 533 ++++++++++++++++++++++++++++++++++++++++++++
mesh/mesh-io-unit.h | 11 +
mesh/mesh-io.c | 9 +-
mesh/mesh-io.h | 3 +-
6 files changed, 582 insertions(+), 17 deletions(-)
create mode 100644 mesh/mesh-io-unit.c
create mode 100644 mesh/mesh-io-unit.h

diff --git a/Makefile.mesh b/Makefile.mesh
index 228dd1b5f..73eaded4a 100644
--- a/Makefile.mesh
+++ b/Makefile.mesh
@@ -17,6 +17,8 @@ mesh_sources = mesh/mesh.h mesh/mesh.c \
mesh/error.h mesh/mesh-io-api.h \
mesh/mesh-io-generic.h \
mesh/mesh-io-generic.c \
+ mesh/mesh-io-unit.h \
+ mesh/mesh-io-unit.c \
mesh/net.h mesh/net.c \
mesh/crypto.h mesh/crypto.c \
mesh/friend.h mesh/friend.c \
diff --git a/mesh/main.c b/mesh/main.c
index 4356e3f65..1b466598b 100644
--- a/mesh/main.c
+++ b/mesh/main.c
@@ -61,7 +61,7 @@ static void usage(void)
"\t--help Show %s information\n", __func__);
fprintf(stderr,
"io:\n"
- "\t([hci]<index> | generic[:[hci]<index>])\n"
+ "\t([hci]<index> | generic[:[hci]<index>] | unit:<fd_path>)\n"
"\t\tUse generic HCI io on interface hci<index>, or the first\n"
"\t\tavailable one\n");
}
@@ -77,6 +77,7 @@ static void mesh_ready_callback(void *user_data, bool success)
{
struct l_dbus *dbus = user_data;

+ l_info("mesh_ready_callback");
if (!success) {
l_error("Failed to start mesh");
l_main_quit();
@@ -92,10 +93,8 @@ static void mesh_ready_callback(void *user_data, bool success)
static void request_name_callback(struct l_dbus *dbus, bool success,
bool queued, void *user_data)
{
- l_info("Request name %s",
- success ? "success": "failed");
-
- if (!success) {
+ if (!success && io_type != MESH_IO_TYPE_UNIT_TEST) {
+ l_info("Request name failed");
l_main_quit();
return;
}
@@ -159,6 +158,21 @@ static bool parse_io(const char *optarg, enum mesh_io_type *type, void **opts)
return true;

return false;
+
+ } else if (strstr(optarg, "unit") == optarg) {
+ char *test_path;
+
+ *type = MESH_IO_TYPE_UNIT_TEST;
+
+ optarg += strlen("unit");
+ if (*optarg != ':')
+ return false;
+
+ optarg++;
+ test_path = strdup(optarg);
+
+ *opts = test_path;
+ return true;
}

return false;
@@ -187,11 +201,19 @@ int main(int argc, char *argv[])
for (;;) {
int opt;

- opt = getopt_long(argc, argv, "i:s:c:ndbh", main_options, NULL);
+ opt = getopt_long(argc, argv, "u:i:s:c:ndbh", main_options,
+ NULL);
if (opt < 0)
break;

switch (opt) {
+ case 'u':
+ if (sscanf(optarg, "%d", &hci_index) == 1 ||
+ sscanf(optarg, "%d", &hci_index) == 1)
+ io = l_strdup_printf("unit:%d", hci_index);
+ else
+ io = l_strdup(optarg);
+ break;
case 'i':
if (sscanf(optarg, "hci%d", &hci_index) == 1 ||
sscanf(optarg, "%d", &hci_index) == 1)
@@ -261,11 +283,8 @@ int main(int argc, char *argv[])
status = l_main_run_with_signal(signal_handler, NULL);

done:
- if (io)
- l_free(io);
-
- if (io_opts)
- l_free(io_opts);
+ l_free(io);
+ l_free(io_opts);

mesh_cleanup();
l_dbus_destroy(dbus);
diff --git a/mesh/mesh-io-unit.c b/mesh/mesh-io-unit.c
new file mode 100644
index 000000000..c5aae6741
--- /dev/null
+++ b/mesh/mesh-io-unit.c
@@ -0,0 +1,533 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <ell/ell.h>
+
+#include "mesh/mesh-defs.h"
+#include "mesh/dbus.h"
+#include "mesh/mesh-io.h"
+#include "mesh/mesh-io-api.h"
+#include "mesh/mesh-io-generic.h"
+
+struct mesh_io_private {
+ struct l_io *sio;
+ void *user_data;
+ char *unique_name;
+ mesh_io_ready_func_t ready_callback;
+ struct l_timeout *tx_timeout;
+ struct l_queue *rx_regs;
+ struct l_queue *tx_pkts;
+ struct sockaddr_un addr;
+ int fd;
+ uint16_t interval;
+};
+
+struct pvt_rx_reg {
+ mesh_io_recv_func_t cb;
+ void *user_data;
+ uint8_t len;
+ uint8_t filter[0];
+};
+
+struct process_data {
+ struct mesh_io_private *pvt;
+ const uint8_t *data;
+ uint8_t len;
+ struct mesh_io_recv_info info;
+};
+
+struct tx_pkt {
+ struct mesh_io_send_info info;
+ bool delete;
+ uint8_t len;
+ uint8_t pkt[30];
+};
+
+struct tx_pattern {
+ const uint8_t *data;
+ uint8_t len;
+};
+
+static uint32_t get_instant(void)
+{
+ struct timeval tm;
+ uint32_t instant;
+
+ gettimeofday(&tm, NULL);
+ instant = tm.tv_sec * 1000;
+ instant += tm.tv_usec / 1000;
+
+ return instant;
+}
+
+static uint32_t instant_remaining_ms(uint32_t instant)
+{
+ instant -= get_instant();
+ return instant;
+}
+
+static void process_rx_callbacks(void *v_reg, void *v_rx)
+{
+ struct pvt_rx_reg *rx_reg = v_reg;
+ struct process_data *rx = v_rx;
+
+ if (!memcmp(rx->data, rx_reg->filter, rx_reg->len))
+ rx_reg->cb(rx_reg->user_data, &rx->info, rx->data, rx->len);
+}
+
+static void process_rx(struct mesh_io_private *pvt, int8_t rssi,
+ uint32_t instant, const uint8_t *addr,
+ const uint8_t *data, uint8_t len)
+{
+ struct process_data rx = {
+ .pvt = pvt,
+ .data = data,
+ .len = len,
+ .info.instant = instant,
+ .info.addr = addr,
+ .info.chan = 7,
+ .info.rssi = rssi,
+ };
+
+ l_queue_foreach(pvt->rx_regs, process_rx_callbacks, &rx);
+}
+
+static bool incoming(struct l_io *sio, void *user_data)
+{
+ struct mesh_io_private *pvt = user_data;
+ uint32_t instant;
+ uint8_t buf[31];
+ size_t size;
+
+ instant = get_instant();
+
+ size = recv(pvt->fd, buf, sizeof(buf), MSG_DONTWAIT);
+
+ if (size > 9 && buf[0]) {
+ process_rx(pvt, -20, instant, NULL, buf + 1, (uint8_t)size);
+ } else if (size == 1 && !buf[0] && pvt->unique_name) {
+
+ /* Return DBUS unique name */
+ size = strlen(pvt->unique_name);
+
+ if (size > sizeof(buf) - 2)
+ return true;
+
+ buf[0] = 0;
+ memcpy(buf + 1, pvt->unique_name, size + 1);
+ send(pvt->fd, buf, size + 2, MSG_DONTWAIT);
+ }
+
+ return true;
+}
+
+static bool find_by_ad_type(const void *a, const void *b)
+{
+ const struct tx_pkt *tx = a;
+ uint8_t ad_type = L_PTR_TO_UINT(b);
+
+ return !ad_type || ad_type == tx->pkt[0];
+}
+
+static bool find_by_pattern(const void *a, const void *b)
+{
+ const struct tx_pkt *tx = a;
+ const struct tx_pattern *pattern = b;
+
+ if (tx->len < pattern->len)
+ return false;
+
+ return (!memcmp(tx->pkt, pattern->data, pattern->len));
+}
+
+static void free_socket(struct mesh_io_private *pvt)
+{
+ l_io_destroy(pvt->sio);
+ close(pvt->fd);
+ unlink(pvt->addr.sun_path);
+}
+
+static void hello_callback(struct l_dbus_message *msg, void *user_data)
+{
+ struct mesh_io_private *pvt = user_data;
+
+ pvt->unique_name = l_strdup(l_dbus_message_get_destination(msg));
+ l_debug("User-Daemon unique name: %s", pvt->unique_name);
+}
+
+static void get_name(struct l_timeout *timeout, void *user_data)
+{
+ struct mesh_io_private *pvt = user_data;
+ struct l_dbus *dbus = dbus_get_bus();
+ struct l_dbus_message *msg;
+
+ l_timeout_remove(timeout);
+ if (!dbus) {
+ l_timeout_create_ms(20, get_name, pvt, NULL);
+ return;
+ }
+
+ /* Retrieve unique name */
+ msg = l_dbus_message_new_method_call(dbus, "org.freedesktop.DBus",
+ "/org/freedesktop/DBus",
+ "org.freedesktop.DBus",
+ "GetId");
+
+ l_dbus_message_set_arguments(msg, "");
+
+ l_dbus_send_with_reply(dbus, msg, hello_callback, pvt, NULL);
+}
+
+static void unit_up(void *user_data)
+{
+ struct mesh_io_private *pvt = user_data;
+
+ l_debug("Started io-unit");
+
+ if (pvt->ready_callback)
+ pvt->ready_callback(pvt->user_data, true);
+
+ l_timeout_create_ms(1, get_name, pvt, NULL);
+}
+
+static bool unit_init(struct mesh_io *io, void *opt,
+ mesh_io_ready_func_t cb, void *user_data)
+{
+ struct mesh_io_private *pvt;
+ char *sk_path;
+ size_t size;
+
+ l_debug("Starting Unit test IO");
+ if (!io || io->pvt)
+ return false;
+
+ sk_path = (char *) opt;
+
+ pvt = l_new(struct mesh_io_private, 1);
+
+ pvt->addr.sun_family = AF_LOCAL;
+ snprintf(pvt->addr.sun_path, sizeof(pvt->addr.sun_path), "%s",
+ sk_path);
+
+ pvt->fd = socket(PF_LOCAL, SOCK_DGRAM | SOCK_CLOEXEC, 0);
+ if (pvt->fd < 0)
+ goto fail;
+
+ unlink(pvt->addr.sun_path);
+ size = offsetof(struct sockaddr_un, sun_path) +
+ strlen(pvt->addr.sun_path);
+
+ if (bind(pvt->fd, (struct sockaddr *) &pvt->addr, size) < 0)
+ goto fail;
+
+ /* Setup socket handlers */
+ pvt->sio = l_io_new(pvt->fd);
+ if (!l_io_set_read_handler(pvt->sio, incoming, pvt, NULL))
+ goto fail;
+
+ pvt->rx_regs = l_queue_new();
+ pvt->tx_pkts = l_queue_new();
+
+ pvt->ready_callback = cb;
+ pvt->user_data = user_data;
+
+ io->pvt = pvt;
+
+ l_idle_oneshot(unit_up, pvt, NULL);
+
+ return true;
+
+fail:
+ l_error("Failed to bind Unit Test socket");
+ free_socket(pvt);
+ l_free(pvt);
+
+ return false;
+}
+
+static bool unit_destroy(struct mesh_io *io)
+{
+ struct mesh_io_private *pvt = io->pvt;
+
+ if (!pvt)
+ return true;
+
+ l_free(pvt->unique_name);
+ l_timeout_remove(pvt->tx_timeout);
+ l_queue_destroy(pvt->rx_regs, l_free);
+ l_queue_destroy(pvt->tx_pkts, l_free);
+
+ free_socket(pvt);
+
+ l_free(pvt);
+ io->pvt = NULL;
+
+ return true;
+}
+
+static bool unit_caps(struct mesh_io *io, struct mesh_io_caps *caps)
+{
+ struct mesh_io_private *pvt = io->pvt;
+
+ if (!pvt || !caps)
+ return false;
+
+ caps->max_num_filters = 255;
+ caps->window_accuracy = 50;
+
+ return true;
+}
+
+static bool simple_match(const void *a, const void *b)
+{
+ return a == b;
+}
+
+static void send_pkt(struct mesh_io_private *pvt, struct tx_pkt *tx,
+ uint16_t interval)
+{
+ send(pvt->fd, tx->pkt, tx->len, MSG_DONTWAIT);
+
+ if (tx->delete) {
+ l_queue_remove_if(pvt->tx_pkts, simple_match, tx);
+ l_free(tx);
+ }
+}
+
+static void tx_to(struct l_timeout *timeout, void *user_data)
+{
+ struct mesh_io_private *pvt = user_data;
+ struct tx_pkt *tx;
+ uint16_t ms;
+ uint8_t count;
+
+ if (!pvt)
+ return;
+
+ tx = l_queue_pop_head(pvt->tx_pkts);
+ if (!tx) {
+ l_timeout_remove(timeout);
+ pvt->tx_timeout = NULL;
+ return;
+ }
+
+ if (tx->info.type == MESH_IO_TIMING_TYPE_GENERAL) {
+ ms = tx->info.u.gen.interval;
+ count = tx->info.u.gen.cnt;
+ if (count != MESH_IO_TX_COUNT_UNLIMITED)
+ tx->info.u.gen.cnt--;
+ } else {
+ ms = 25;
+ count = 1;
+ }
+
+ tx->delete = !!(count == 1);
+
+ send_pkt(pvt, tx, ms);
+
+ if (count == 1) {
+ /* Recalculate wakeup if we are responding to POLL */
+ tx = l_queue_peek_head(pvt->tx_pkts);
+
+ if (tx && tx->info.type == MESH_IO_TIMING_TYPE_POLL_RSP) {
+ ms = instant_remaining_ms(tx->info.u.poll_rsp.instant +
+ tx->info.u.poll_rsp.delay);
+ }
+ } else
+ l_queue_push_tail(pvt->tx_pkts, tx);
+
+ if (timeout) {
+ pvt->tx_timeout = timeout;
+ l_timeout_modify_ms(timeout, ms);
+ } else
+ pvt->tx_timeout = l_timeout_create_ms(ms, tx_to, pvt, NULL);
+}
+
+static void tx_worker(void *user_data)
+{
+ struct mesh_io_private *pvt = user_data;
+ struct tx_pkt *tx;
+ uint32_t delay;
+
+ tx = l_queue_peek_head(pvt->tx_pkts);
+ if (!tx)
+ return;
+
+ switch (tx->info.type) {
+ case MESH_IO_TIMING_TYPE_GENERAL:
+ if (tx->info.u.gen.min_delay == tx->info.u.gen.max_delay)
+ delay = tx->info.u.gen.min_delay;
+ else {
+ l_getrandom(&delay, sizeof(delay));
+ delay %= tx->info.u.gen.max_delay -
+ tx->info.u.gen.min_delay;
+ delay += tx->info.u.gen.min_delay;
+ }
+ break;
+
+ case MESH_IO_TIMING_TYPE_POLL:
+ if (tx->info.u.poll.min_delay == tx->info.u.poll.max_delay)
+ delay = tx->info.u.poll.min_delay;
+ else {
+ l_getrandom(&delay, sizeof(delay));
+ delay %= tx->info.u.poll.max_delay -
+ tx->info.u.poll.min_delay;
+ delay += tx->info.u.poll.min_delay;
+ }
+ break;
+
+ case MESH_IO_TIMING_TYPE_POLL_RSP:
+ /* Delay until Instant + Delay */
+ delay = instant_remaining_ms(tx->info.u.poll_rsp.instant +
+ tx->info.u.poll_rsp.delay);
+ if (delay > 255)
+ delay = 0;
+ break;
+
+ default:
+ return;
+ }
+
+ if (!delay)
+ tx_to(pvt->tx_timeout, pvt);
+ else if (pvt->tx_timeout)
+ l_timeout_modify_ms(pvt->tx_timeout, delay);
+ else
+ pvt->tx_timeout = l_timeout_create_ms(delay, tx_to, pvt, NULL);
+}
+
+static bool send_tx(struct mesh_io *io, struct mesh_io_send_info *info,
+ const uint8_t *data, uint16_t len)
+{
+ struct mesh_io_private *pvt = io->pvt;
+ struct tx_pkt *tx;
+ bool sending = false;
+
+ if (!info || !data || !len || len > sizeof(tx->pkt))
+ return false;
+
+ tx = l_new(struct tx_pkt, 1);
+
+ memcpy(&tx->info, info, sizeof(tx->info));
+ memcpy(&tx->pkt, data, len);
+ tx->len = len;
+
+ if (info->type == MESH_IO_TIMING_TYPE_POLL_RSP)
+ l_queue_push_head(pvt->tx_pkts, tx);
+ else {
+ sending = !l_queue_isempty(pvt->tx_pkts);
+
+ l_queue_push_tail(pvt->tx_pkts, tx);
+ }
+
+ if (!sending) {
+ l_timeout_remove(pvt->tx_timeout);
+ pvt->tx_timeout = NULL;
+ l_idle_oneshot(tx_worker, pvt, NULL);
+ }
+
+ return true;
+}
+
+static bool tx_cancel(struct mesh_io *io, const uint8_t *data, uint8_t len)
+{
+ struct mesh_io_private *pvt = io->pvt;
+ struct tx_pkt *tx;
+
+ if (!data)
+ return false;
+
+ if (len == 1) {
+ do {
+ tx = l_queue_remove_if(pvt->tx_pkts, find_by_ad_type,
+ L_UINT_TO_PTR(data[0]));
+ l_free(tx);
+
+ } while (tx);
+ } else {
+ struct tx_pattern pattern = {
+ .data = data,
+ .len = len
+ };
+
+ do {
+ tx = l_queue_remove_if(pvt->tx_pkts, find_by_pattern,
+ &pattern);
+ l_free(tx);
+
+ } while (tx);
+ }
+
+ if (l_queue_isempty(pvt->tx_pkts)) {
+ l_timeout_remove(pvt->tx_timeout);
+ pvt->tx_timeout = NULL;
+ }
+
+ return true;
+}
+
+static bool find_by_filter(const void *a, const void *b)
+{
+ const struct pvt_rx_reg *rx_reg = a;
+ const uint8_t *filter = b;
+
+ return !memcmp(rx_reg->filter, filter, rx_reg->len);
+}
+
+static bool recv_register(struct mesh_io *io, const uint8_t *filter,
+ uint8_t len, mesh_io_recv_func_t cb, void *user_data)
+{
+ struct mesh_io_private *pvt = io->pvt;
+ struct pvt_rx_reg *rx_reg;
+
+ if (!cb || !filter || !len)
+ return false;
+
+ rx_reg = l_queue_remove_if(pvt->rx_regs, find_by_filter, filter);
+
+ l_free(rx_reg);
+ rx_reg = l_malloc(sizeof(*rx_reg) + len);
+
+ memcpy(rx_reg->filter, filter, len);
+ rx_reg->len = len;
+ rx_reg->cb = cb;
+ rx_reg->user_data = user_data;
+
+ l_queue_push_head(pvt->rx_regs, rx_reg);
+
+ return true;
+}
+
+static bool recv_deregister(struct mesh_io *io, const uint8_t *filter,
+ uint8_t len)
+{
+ return true;
+}
+
+const struct mesh_io_api mesh_io_unit = {
+ .init = unit_init,
+ .destroy = unit_destroy,
+ .caps = unit_caps,
+ .send = send_tx,
+ .reg = recv_register,
+ .dereg = recv_deregister,
+ .cancel = tx_cancel,
+};
diff --git a/mesh/mesh-io-unit.h b/mesh/mesh-io-unit.h
new file mode 100644
index 000000000..846eea7bc
--- /dev/null
+++ b/mesh/mesh-io-unit.h
@@ -0,0 +1,11 @@
+/* SPDX-License-Identifier: LGPL-2.1-or-later */
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+extern const struct mesh_io_api mesh_io_unit;
diff --git a/mesh/mesh-io.c b/mesh/mesh-io.c
index 62fc5d12e..96891313a 100644
--- a/mesh/mesh-io.c
+++ b/mesh/mesh-io.c
@@ -22,10 +22,12 @@

/* List of Mesh-IO Type headers */
#include "mesh/mesh-io-generic.h"
+#include "mesh/mesh-io-unit.h"

/* List of Supported Mesh-IO Types */
static const struct mesh_io_table table[] = {
- {MESH_IO_TYPE_GENERIC, &mesh_io_generic}
+ {MESH_IO_TYPE_GENERIC, &mesh_io_generic},
+ {MESH_IO_TYPE_UNIT_TEST, &mesh_io_unit},
};

static struct l_queue *io_list;
@@ -64,12 +66,9 @@ struct mesh_io *mesh_io_new(enum mesh_io_type type, void *opts,

io = l_new(struct mesh_io, 1);

- if (!io)
- return NULL;
-
io->type = type;
-
io->api = api;
+
if (!api->init(io, opts, cb, user_data))
goto fail;

diff --git a/mesh/mesh-io.h b/mesh/mesh-io.h
index b11c6c6e1..80ef3fa3e 100644
--- a/mesh/mesh-io.h
+++ b/mesh/mesh-io.h
@@ -14,7 +14,8 @@ struct mesh_io;

enum mesh_io_type {
MESH_IO_TYPE_NONE = 0,
- MESH_IO_TYPE_GENERIC
+ MESH_IO_TYPE_GENERIC,
+ MESH_IO_TYPE_UNIT_TEST
};

enum mesh_io_timing_type {
--
2.26.2

2021-03-19 20:41:23

by Stotland, Inga

[permalink] [raw]
Subject: [PATCH BlueZ v4 2/2] tools/mesh-cfgtest: Non-iteractive test for mesh

This adds a non-interactive test to excercise different datapaths in
bluetooth-meshd. The test cases utilize D-Bus based mesh APIs, e.g.,
to create a new network, import a node, import NetKey, import a remote
node.
---
Makefile.am | 14 +-
Makefile.tools | 6 +
mesh/main.c | 10 +-
tools/mesh-cfgtest.c | 1458 ++++++++++++++++++++++++++++++++++++++++++
4 files changed, 1483 insertions(+), 5 deletions(-)
create mode 100644 tools/mesh-cfgtest.c

diff --git a/Makefile.am b/Makefile.am
index 7fce2e7c0..e70c61a81 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -115,6 +115,8 @@ ell_headers = ell/util.h \
ell/hashmap.h \
ell/random.h \
ell/signal.h \
+ ell/time.h \
+ ell/time-private.h \
ell/timeout.h \
ell/cipher.h \
ell/checksum.h \
@@ -137,7 +139,8 @@ ell_headers = ell/util.h \
ell/pem-private.h \
ell/uuid.h \
ell/useful.h \
- ell/main-private.h
+ ell/main-private.h \
+ ell/tester.h

ell_sources = ell/private.h ell/missing.h \
ell/util.c \
@@ -146,6 +149,7 @@ ell_sources = ell/private.h ell/missing.h \
ell/hashmap.c \
ell/random.c \
ell/signal.c \
+ ell/time.c \
ell/timeout.c \
ell/io.c \
ell/idle.c \
@@ -173,7 +177,8 @@ ell_sources = ell/private.h ell/missing.h \
ell/gvariant-util.c \
ell/siphash-private.h \
ell/siphash.c \
- ell/uuid.c
+ ell/uuid.c \
+ ell/tester.c

ell_libell_internal_la_SOURCES = $(ell_headers) $(ell_sources)
endif
@@ -193,7 +198,7 @@ shared_sources = src/shared/io.h src/shared/timeout.h \
src/shared/crypto.h src/shared/crypto.c \
src/shared/ecc.h src/shared/ecc.c \
src/shared/ringbuf.h src/shared/ringbuf.c \
- src/shared/tester.h src/shared/tester.c \
+ src/shared/tester.h\
src/shared/hci.h src/shared/hci.c \
src/shared/hci-crypto.h src/shared/hci-crypto.c \
src/shared/hfp.h src/shared/hfp.c \
@@ -220,7 +225,8 @@ src_libshared_glib_la_SOURCES = $(shared_sources) \
src/shared/timeout-glib.c \
src/shared/mainloop-glib.c \
src/shared/mainloop-notify.h \
- src/shared/mainloop-notify.c
+ src/shared/mainloop-notify.c \
+ src/shared/tester.c

src_libshared_mainloop_la_SOURCES = $(shared_sources) \
src/shared/io-mainloop.c \
diff --git a/Makefile.tools b/Makefile.tools
index d5fdf2d89..3217ca8a6 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -335,6 +335,12 @@ tools_mesh_cfgclient_SOURCES = tools/mesh-cfgclient.c \

tools_mesh_cfgclient_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
$(ell_ldadd) -ljson-c -lreadline
+
+bin_PROGRAMS += tools/mesh-cfgtest
+
+tools_mesh_cfgtest_SOURCES = tools/mesh-cfgtest.c
+tools_mesh_cfgtest_LDADD = lib/libbluetooth-internal.la src/libshared-ell.la \
+ $(ell_ldadd)
endif

EXTRA_DIST += tools/mesh-gatt/local_node.json tools/mesh-gatt/prov_db.json
diff --git a/mesh/main.c b/mesh/main.c
index 1b466598b..a13866d7e 100644
--- a/mesh/main.c
+++ b/mesh/main.c
@@ -17,7 +17,9 @@
#include <stdlib.h>
#include <unistd.h>
#include <ctype.h>
+#include <signal.h>

+#include <sys/prctl.h>
#include <sys/stat.h>
#include <ell/ell.h>

@@ -262,7 +264,13 @@ int main(int argc, char *argv[])
if (!detached)
umask(0077);

- dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+ if (io_type != MESH_IO_TYPE_UNIT_TEST)
+ dbus = l_dbus_new_default(L_DBUS_SYSTEM_BUS);
+ else {
+ dbus = l_dbus_new_default(L_DBUS_SESSION_BUS);
+ prctl(PR_SET_PDEATHSIG, SIGSEGV);
+ }
+
if (!dbus) {
l_error("unable to connect to D-Bus");
status = EXIT_FAILURE;
diff --git a/tools/mesh-cfgtest.c b/tools/mesh-cfgtest.c
new file mode 100644
index 000000000..fa0474faa
--- /dev/null
+++ b/tools/mesh-cfgtest.c
@@ -0,0 +1,1458 @@
+// SPDX-License-Identifier: LGPL-2.1-or-later
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2021 Intel Corporation. All rights reserved.
+ *
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#define _GNU_SOURCE
+#include <assert.h>
+#include <ctype.h>
+#include <dbus/dbus.h>
+#include <dirent.h>
+#include <errno.h>
+#include <ftw.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#include <ell/ell.h>
+
+#include "src/shared/util.h"
+
+#include "mesh/mesh-defs.h"
+#include "mesh/mesh.h"
+
+#define MAX_CRPL_SIZE 0x7fff
+#define CFG_SRV_MODEL 0x0000
+#define CFG_CLI_MODEL 0x0001
+#define DEFAULT_IV_INDEX 0x0000
+
+#define IS_CONFIG_MODEL(x) ((x) == CFG_SRV_MODEL || (x) == CFG_CLI_MODEL)
+
+struct meshcfg_el {
+ const char *path;
+ uint8_t index;
+ uint16_t mods[2];
+ uint32_t vmods[2];
+};
+
+struct meshcfg_app {
+ const char *path;
+ const char *agent_path;
+ struct meshcfg_node *node;
+ uint8_t num_ele;
+ struct meshcfg_el ele[2];
+ uint16_t cid;
+ uint16_t pid;
+ uint16_t vid;
+ uint16_t crpl;
+ uint8_t uuid[16];
+};
+
+struct meshcfg_node {
+ const char *path;
+ struct l_dbus_proxy *proxy;
+ struct l_dbus_proxy *mgmt_proxy;
+ union {
+ uint64_t u64;
+ uint8_t u8[8];
+ } token;
+};
+
+struct msg_data {
+ uint16_t len;
+ uint8_t data[MAX_MSG_LEN];
+};
+
+struct key_data {
+ uint16_t idx;
+ bool update;
+};
+
+typedef void (*startup_func_t)(const void *data);
+struct startup_entry {
+ startup_func_t func;
+ void *data;
+};
+
+struct test_data {
+ const char *ele_path;
+ uint16_t dst;
+ uint16_t subnet;
+ void *req;
+};
+
+static struct l_tester *tester;
+
+static bool option_list;
+static const char *option_prefix;
+static const char *option_string;
+static char *tester_name;
+
+static bool init_done;
+static bool init_failed;
+static bool terminated;
+
+static struct l_queue *startup_chain;
+
+static struct l_dbus *dbus;
+struct l_dbus_client *client;
+
+static struct l_queue *node_proxies;
+static struct l_dbus_proxy *net_proxy;
+static char *test_dir;
+static char *io;
+static char *exe;
+
+static uint32_t iv_index = DEFAULT_IV_INDEX;
+
+static const char *dbus_err_args = "org.freedesktop.DBus.Error.InvalidArgs";
+static const char *const cli_app_path = "/mesh/cfgtest/client";
+static const char *const cli_agent_path = "/mesh/cfgtest/client/agent";
+static const char *const cli_ele_path_00 = "/mesh/cfgtest/client/ele0";
+static const char *const srv_app_path = "/mesh/cfgtest/server";
+static const char *const srv_agent_path = "/mesh/cfgtest/server/agent";
+static const char *const srv_ele_path_00 = "/mesh/cfgtest/server/ele0";
+static const char *const srv_ele_path_01 = "/mesh/cfgtest/server/ele1";
+
+static struct meshcfg_app client_app = {
+ .path = cli_app_path,
+ .agent_path = cli_agent_path,
+ .cid = 0x05f1,
+ .pid = 0x0002,
+ .vid = 0x0001,
+ .crpl = MAX_CRPL_SIZE,
+ .num_ele = 1,
+ .ele = {
+ {
+ .path = cli_ele_path_00,
+ .index = PRIMARY_ELE_IDX,
+ .mods = {CFG_SRV_MODEL, CFG_CLI_MODEL},
+ .vmods = {0xffffffff, 0xffffffff}
+ }
+ }
+};
+
+static struct meshcfg_app server_app = {
+ .path = srv_app_path,
+ .agent_path = srv_agent_path,
+ .cid = 0x05f1,
+ .pid = 0x0002,
+ .vid = 0x0001,
+ .crpl = MAX_CRPL_SIZE,
+ .num_ele = 2,
+ .ele = {
+ {
+ .path = srv_ele_path_00,
+ .index = PRIMARY_ELE_IDX,
+ .mods = {CFG_SRV_MODEL, 0xffff},
+ .vmods = {0xffffffff, 0xffffffff}
+ },
+ {
+ .path = srv_ele_path_01,
+ .index = PRIMARY_ELE_IDX + 1,
+ .mods = {0x1000, 0xffff},
+ .vmods = {0x5F10001, 0xffffffff}
+ }
+ }
+};
+
+static uint8_t import_devkey[16];
+static uint8_t import_netkey[16];
+static const uint16_t import_netkey_idx = 0x001;
+static const uint16_t import_node_unicast = 0xbcd;
+
+static void create_network(const void *data);
+static struct startup_entry init_create_client = {
+ .func = create_network,
+ .data = NULL,
+};
+
+static void import_node(const void *data);
+static struct startup_entry init_import_server = {
+ .func = import_node,
+ .data = NULL,
+};
+
+static void attach_node(const void *data);
+static struct startup_entry init_attach_client = {
+ .func = attach_node,
+ .data = NULL,
+};
+
+static void import_subnet(const void *data);
+static struct startup_entry init_import_subnet = {
+ .func = import_subnet,
+ .data = NULL,
+};
+
+static void import_remote(const void *data);
+static struct startup_entry init_import_remote = {
+ .func = import_remote,
+ .data = NULL,
+};
+
+static struct msg_data init_add_netkey_rsp = {
+ .len = 5,
+ .data = {0x80, 0x44, 0x00, 0x01, 0x00}
+};
+
+static struct key_data init_add_netkey_req = {
+ .idx = import_netkey_idx,
+ .update = false
+};
+
+static struct test_data init_add_netkey_data = {
+ .ele_path = cli_ele_path_00,
+ .dst = 0x0001,
+ .subnet = 0x0000,
+ .req = &init_add_netkey_req
+};
+
+static void add_netkey(const void *data);
+static struct startup_entry init_add_netkey = {
+ .func = add_netkey,
+ .data = &init_add_netkey_data
+};
+
+static struct msg_data init_add_appkey_rsp = {
+ .len = 6,
+ .data = {0x80, 0x03, 0x00, 0x01, 0x10, 0x00}
+};
+
+static struct key_data init_add_appkey_req = {
+ .idx = 0x001,
+ .update = false
+};
+
+static struct test_data init_add_appkey_data = {
+ .ele_path = cli_ele_path_00,
+ .dst = import_node_unicast,
+ .subnet = import_netkey_idx,
+ .req = &init_add_appkey_req,
+};
+
+static void create_appkey(const void *data);
+static struct startup_entry init_create_appkey = {
+ .func = create_appkey,
+ .data = &init_add_appkey_data
+};
+
+static void add_appkey(const void *data);
+static struct startup_entry init_add_appkey = {
+ .func = add_appkey,
+ .data = &init_add_appkey_data
+};
+
+static struct msg_data test_add_appkey_rsp = {
+ .len = 6,
+ .data = {0x80, 0x03, 0x00, 0x01, 0x20, 0x00}
+};
+
+static struct key_data test_add_appkey_req = {
+ .idx = 0x002,
+ .update = false
+};
+
+static struct test_data test_add_appkey = {
+ .ele_path = cli_ele_path_00,
+ .dst = import_node_unicast,
+ .subnet = import_netkey_idx,
+ .req = &test_add_appkey_req,
+};
+
+static struct test_data common_route = {
+ .ele_path = cli_ele_path_00,
+ .dst = import_node_unicast,
+ .subnet = import_netkey_idx,
+};
+
+static struct msg_data test_set_ttl_rsp = {
+ .len = 3,
+ .data = { 0x80, 0x0E, 0x7}
+};
+
+static struct msg_data test_set_ttl_req = {
+ .len = 3,
+ .data = { 0x80, 0x0D, 0x7}
+};
+
+static struct msg_data test_bind_rsp = {
+ .len = 9,
+ .data = { 0x80, 0x3E, 0x00, 0xCE, 0x0B, 0x01, 0x00, 0x00, 0x10},
+};
+
+static struct msg_data test_bind_req = {
+ .len = 8,
+ .data = { 0x80, 0x3D, 0xCE, 0x0B, 0x01, 0x00, 0x00, 0x10}
+};
+
+
+static struct msg_data test_bind_inv_mod_rsp = {
+ .len = 9,
+ .data = { 0x80, 0x3E, 0x02, 0xCE, 0x0B, 0x01, 0x00, 0x00, 0x11},
+};
+
+static struct msg_data test_bind_inv_mod_req = {
+ .len = 8,
+ .data = { 0x80, 0x3D, 0xCE, 0x0B, 0x01, 0x00, 0x00, 0x11}
+};
+
+static struct msg_data test_dev_comp_rsp = {
+ .len = 28,
+ .data = { 0x02, 0x00, 0xf1, 0x05, 0x02, 0x00, 0x01, 0x00, 0xff, 0x7f,
+ 0x05, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x01, 0x01, 0x00, 0x10, 0xf1, 0x05, 0x01, 0x00}
+};
+
+static struct msg_data test_dev_comp_req = {
+ .len = 3,
+ .data = {0x80, 0x08, 0x00}
+};
+
+static void append_byte_array(struct l_dbus_message_builder *builder,
+ unsigned char *data, unsigned int len)
+{
+ unsigned int i;
+
+ l_dbus_message_builder_enter_array(builder, "y");
+
+ for (i = 0; i < len; i++)
+ l_dbus_message_builder_append_basic(builder, 'y', &(data[i]));
+
+ l_dbus_message_builder_leave_array(builder);
+}
+
+static void append_dict_entry_basic(struct l_dbus_message_builder *builder,
+ const char *key, const char *signature,
+ const void *data)
+{
+ if (!builder)
+ return;
+
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_append_basic(builder, 's', key);
+ l_dbus_message_builder_enter_variant(builder, signature);
+ l_dbus_message_builder_append_basic(builder, signature[0], data);
+ l_dbus_message_builder_leave_variant(builder);
+ l_dbus_message_builder_leave_dict(builder);
+}
+
+static void init_continue(void *data)
+{
+ struct startup_entry *next_step;
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (l_queue_isempty(startup_chain) &&
+ stage == L_TESTER_STAGE_PRE_SETUP) {
+ l_tester_pre_setup_complete(tester);
+ init_done = true;
+ return;
+ }
+
+ next_step = l_queue_pop_head(startup_chain);
+
+ next_step->func(next_step->data);
+}
+
+static void test_success(void *user_data)
+{
+ l_tester_test_passed(tester);
+}
+
+static void test_fail(void *user_data)
+{
+ l_tester_test_failed(tester);
+}
+
+static void try_set_node_proxy(void *a, void *b)
+{
+ struct l_dbus_proxy *proxy = a;
+ struct meshcfg_node *node = b;
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ if (strcmp(node->path, path))
+ return;
+
+ if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE))
+ node->mgmt_proxy = proxy;
+ else if (!strcmp(interface, MESH_NODE_INTERFACE))
+ node->proxy = proxy;
+}
+
+static void generic_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_error("D-Bus call failed: %s", name);
+ l_idle_oneshot(test_fail, NULL, NULL);
+ }
+}
+
+static void send_cfg_msg_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct msg_data *req = user_data;
+ struct l_dbus_message_builder *builder;
+ bool remote = true;
+
+ builder = l_dbus_message_builder_new(msg);
+
+ l_dbus_message_builder_append_basic(builder, 'o',
+ common_route.ele_path);
+ l_dbus_message_builder_append_basic(builder, 'q', &common_route.dst);
+ l_dbus_message_builder_append_basic(builder, 'b', &remote);
+
+ l_dbus_message_builder_append_basic(builder, 'q', &common_route.subnet);
+
+ /* Options */
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ l_dbus_message_builder_enter_dict(builder, "sv");
+ l_dbus_message_builder_leave_dict(builder);
+ l_dbus_message_builder_leave_array(builder);
+
+ /* Data */
+ append_byte_array(builder, req->data, req->len);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void send_cfg_msg(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ l_dbus_proxy_method_call(node->proxy, "DevKeySend",
+ send_cfg_msg_setup, generic_reply,
+ (void *) data, NULL);
+}
+
+static void add_key_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct test_data *tst = user_data;
+ struct key_data *req = tst->req;
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(msg);
+
+ l_dbus_message_builder_append_basic(builder, 'o', tst->ele_path);
+ l_dbus_message_builder_append_basic(builder, 'q', &tst->dst);
+ l_dbus_message_builder_append_basic(builder, 'q', &req->idx);
+ l_dbus_message_builder_append_basic(builder, 'q', &tst->subnet);
+ l_dbus_message_builder_append_basic(builder, 'b', &req->update);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void add_appkey(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ l_dbus_proxy_method_call(node->proxy, "AddAppKey", add_key_setup,
+ generic_reply, (void *) data, NULL);
+}
+
+static void add_netkey(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ l_dbus_proxy_method_call(node->proxy, "AddNetKey", add_key_setup,
+ generic_reply, (void *) data, NULL);
+}
+
+static void create_appkey_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_info("Add key failed: %s", name);
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP) {
+ init_failed = true;
+ l_tester_pre_setup_failed(tester);
+ } else
+ l_tester_setup_failed(tester);
+ } else {
+ if (stage == L_TESTER_STAGE_PRE_SETUP)
+ l_idle_oneshot(init_continue, NULL, NULL);
+ else
+ l_tester_setup_complete(tester);
+ }
+}
+
+static void create_appkey_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct test_data *tst = user_data;
+ struct key_data *req = tst->req;
+
+ l_dbus_message_set_arguments(msg, "qq", tst->subnet, req->idx);
+}
+
+static void create_appkey(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ if (!node || !node->proxy || !node->mgmt_proxy) {
+ l_info("Node is not attached\n");
+ l_tester_setup_failed(tester);
+ return;
+ }
+
+ l_dbus_proxy_method_call(node->mgmt_proxy, "CreateAppKey",
+ create_appkey_setup, create_appkey_reply,
+ (void *) data, NULL);
+}
+
+static void import_remote_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_info("Import remote call failed: %s", name);
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP) {
+ init_failed = true;
+ l_tester_pre_setup_failed(tester);
+ }
+
+ return;
+ }
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP)
+ l_idle_oneshot(init_continue, NULL, NULL);
+ else
+ l_tester_test_abort(tester);
+}
+
+static void import_remote_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(msg);
+
+ l_dbus_message_builder_append_basic(builder, 'q', &import_node_unicast);
+ l_dbus_message_builder_append_basic(builder, 'y', &server_app.num_ele);
+ append_byte_array(builder, import_devkey, 16);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void import_remote(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ if (!node || !node->mgmt_proxy) {
+ l_tester_test_abort(tester);
+ return;
+ }
+
+ l_dbus_proxy_method_call(node->mgmt_proxy, "ImportRemoteNode",
+ import_remote_setup, import_remote_reply,
+ NULL, NULL);
+}
+
+static void import_subnet_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (stage != L_TESTER_STAGE_PRE_SETUP)
+ return;
+
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_info("Import subnet failed: %s", name);
+
+ init_failed = true;
+ return l_tester_pre_setup_failed(tester);
+ }
+
+ l_idle_oneshot(init_continue, NULL, NULL);
+}
+
+static void import_subnet_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct l_dbus_message_builder *builder;
+
+ builder = l_dbus_message_builder_new(msg);
+
+ l_dbus_message_builder_append_basic(builder, 'q', &import_netkey_idx);
+ append_byte_array(builder, import_netkey, 16);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void import_subnet(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ if (!node || !node->mgmt_proxy) {
+ l_tester_test_abort(tester);
+ return;
+ }
+
+ l_dbus_proxy_method_call(node->mgmt_proxy, "ImportSubnet",
+ import_subnet_setup, import_subnet_reply,
+ NULL, NULL);
+}
+
+static void attach_node_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+ struct meshcfg_node *node = app->node;
+ struct l_dbus_message_iter iter_cfg;
+ uint32_t ivi;
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_error("Failed to attach node: %s", name);
+ goto fail;
+ }
+
+ if (!l_dbus_message_get_arguments(msg, "oa(ya(qa{sv}))",
+ &node->path, &iter_cfg))
+ goto fail;
+
+ l_info("Attached with path %s\n", node->path);
+
+ /* Populate node's proxies */
+ l_queue_foreach(node_proxies, try_set_node_proxy, node);
+
+ /* Remove from orphaned proxies list */
+ if (node->proxy)
+ l_queue_remove(node_proxies, node->proxy);
+
+ if (node->mgmt_proxy)
+ l_queue_remove(node_proxies, node->mgmt_proxy);
+
+ if (l_dbus_proxy_get_property(node->proxy, "IvIndex", "u", &ivi) &&
+ ivi != iv_index)
+ iv_index = ivi;
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP)
+ l_idle_oneshot(init_continue, NULL, NULL);
+
+ return;
+
+fail:
+ l_free(node);
+ app->node = NULL;
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP) {
+ init_failed = true;
+ l_tester_pre_setup_failed(tester);
+ }
+}
+
+static void attach_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+
+ l_dbus_message_set_arguments(msg, "ot", app->path,
+ l_get_be64(app->node->token.u8));
+}
+
+static void attach_node(const void *data)
+{
+ struct meshcfg_node *node = client_app.node;
+
+ if (!node) {
+ l_tester_test_abort(tester);
+ return;
+ }
+
+ l_dbus_proxy_method_call(net_proxy, "Attach",
+ attach_node_setup, attach_node_reply,
+ &client_app, NULL);
+}
+
+static struct l_dbus_message *join_complete(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+ uint64_t tmp;
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (!l_dbus_message_get_arguments(message, "t", &tmp)) {
+ if (stage == L_TESTER_STAGE_PRE_SETUP) {
+ init_failed = true;
+ l_tester_pre_setup_failed(tester);
+ } else
+ l_idle_oneshot(test_fail, NULL, NULL);
+
+ return l_dbus_message_new_error(message, dbus_err_args, NULL);
+ }
+
+ app->node = l_new(struct meshcfg_node, 1);
+ app->node->token.u64 = l_get_be64(&tmp);
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP)
+ l_idle_oneshot(init_continue, NULL, NULL);
+
+ return l_dbus_message_new_method_return(message);
+}
+
+static void create_net_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_error("Failed to create network: %s", name);
+ l_tester_setup_failed(tester);
+ }
+}
+
+static void create_net_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct l_dbus_message_builder *builder;
+
+ /* Generate random UUID */
+ l_uuid_v4(client_app.uuid);
+
+ builder = l_dbus_message_builder_new(msg);
+
+ l_dbus_message_builder_append_basic(builder, 'o', client_app.path);
+ append_byte_array(builder, client_app.uuid, 16);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void create_network(const void *data)
+{
+ l_dbus_proxy_method_call(net_proxy, "CreateNetwork", create_net_setup,
+ create_net_reply, &client_app,
+ NULL);
+}
+
+static void import_node_reply(struct l_dbus_proxy *proxy,
+ struct l_dbus_message *msg, void *user_data)
+{
+ if (l_dbus_message_is_error(msg)) {
+ const char *name;
+
+ l_dbus_message_get_error(msg, &name, NULL);
+ l_error("Failed to import local node: %s", name);
+ l_idle_oneshot(test_fail, NULL, NULL);
+ return;
+ }
+}
+
+static void import_node_setup(struct l_dbus_message *msg, void *user_data)
+{
+ struct l_dbus_message_builder *builder;
+ bool iv_update = false;
+ bool key_refresh = false;
+
+ /* Generate random UUID, DevKey, NetKey */
+ l_uuid_v4(server_app.uuid);
+ l_getrandom(import_netkey, sizeof(import_netkey));
+ l_getrandom(import_devkey, sizeof(import_devkey));
+
+ builder = l_dbus_message_builder_new(msg);
+
+ l_dbus_message_builder_append_basic(builder, 'o', server_app.path);
+ append_byte_array(builder, server_app.uuid, 16);
+ append_byte_array(builder, import_devkey, 16);
+ append_byte_array(builder, import_netkey, 16);
+ l_dbus_message_builder_append_basic(builder, 'q', &import_netkey_idx);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ append_dict_entry_basic(builder, "IvUpdate", "b", &iv_update);
+ append_dict_entry_basic(builder, "KeyRefresh", "b", &key_refresh);
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_append_basic(builder, 'u', &iv_index);
+ l_dbus_message_builder_append_basic(builder, 'q', &import_node_unicast);
+ l_dbus_message_builder_finalize(builder);
+ l_dbus_message_builder_destroy(builder);
+}
+
+static void import_node(const void *data)
+{
+ l_dbus_proxy_method_call(net_proxy, "Import", import_node_setup,
+ import_node_reply, &server_app,
+ NULL);
+}
+
+static void proxy_added(struct l_dbus_proxy *proxy, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("Proxy added: %s (%s)\n", interface, path);
+
+ if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
+ net_proxy = proxy;
+ return;
+ }
+
+ if (!strcmp(interface, MESH_MANAGEMENT_INTERFACE)) {
+ if (client_app.node && client_app.node->path) {
+ if (!strcmp(client_app.node->path, path)) {
+ client_app.node->mgmt_proxy = proxy;
+ return;
+ }
+ }
+
+ if (server_app.node && server_app.node->path) {
+ if (!strcmp(server_app.node->path, path)) {
+ server_app.node->mgmt_proxy = proxy;
+ return;
+ }
+ }
+
+ l_queue_push_tail(node_proxies, proxy);
+ return;
+ }
+
+ if (!strcmp(interface, MESH_NODE_INTERFACE)) {
+
+ if (client_app.node && client_app.node->path) {
+ if (!strcmp(client_app.node->path, path)) {
+ client_app.node->proxy = proxy;
+ return;
+ }
+ }
+
+ if (server_app.node && server_app.node->path) {
+ if (!strcmp(server_app.node->path, path)) {
+ server_app.node->proxy = proxy;
+ return;
+ }
+ }
+
+ l_queue_push_tail(node_proxies, proxy);
+ }
+}
+
+static void proxy_removed(struct l_dbus_proxy *proxy, void *user_data)
+{
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ l_info("Proxy removed: %s (%s)\n", interface, path);
+
+ if (!strcmp(interface, MESH_NETWORK_INTERFACE)) {
+ l_info("Mesh removed, terminating.\n");
+ l_main_quit();
+ return;
+ }
+
+ l_queue_remove(node_proxies, proxy);
+}
+
+static void build_model(struct l_dbus_message_builder *builder, uint16_t mod_id,
+ bool pub_enable, bool sub_enable)
+{
+ l_dbus_message_builder_enter_struct(builder, "qa{sv}");
+ l_dbus_message_builder_append_basic(builder, 'q', &mod_id);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ append_dict_entry_basic(builder, "Subscribe", "b", &sub_enable);
+ append_dict_entry_basic(builder, "Publish", "b", &pub_enable);
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_leave_struct(builder);
+}
+
+static bool mod_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_el *ele = user_data;
+ uint32_t i;
+
+ l_dbus_message_builder_enter_array(builder, "(qa{sv})");
+
+ for (i = 0; i < L_ARRAY_SIZE(ele->mods); i++) {
+ bool is_cfg = IS_CONFIG_MODEL(ele->mods[i]);
+
+ if (ele->mods[i] == 0xffff)
+ continue;
+
+ build_model(builder, ele->mods[i], !is_cfg, !is_cfg);
+ }
+
+ l_dbus_message_builder_leave_array(builder);
+
+ return true;
+}
+
+static void build_vmodel(struct l_dbus_message_builder *builder, uint16_t vid,
+ uint16_t mod, bool pub_enable, bool sub_enable)
+{
+ l_dbus_message_builder_enter_struct(builder, "qqa{sv}");
+ l_dbus_message_builder_append_basic(builder, 'q', &vid);
+ l_dbus_message_builder_append_basic(builder, 'q', &mod);
+ l_dbus_message_builder_enter_array(builder, "{sv}");
+ append_dict_entry_basic(builder, "Subscribe", "b", &sub_enable);
+ append_dict_entry_basic(builder, "Publish", "b", &pub_enable);
+ l_dbus_message_builder_leave_array(builder);
+ l_dbus_message_builder_leave_struct(builder);
+}
+
+static bool vmod_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_el *ele = user_data;
+ uint32_t i;
+
+ l_dbus_message_builder_enter_array(builder, "(qqa{sv})");
+
+ for (i = 0; i < L_ARRAY_SIZE(ele->vmods); i++) {
+ if (ele->vmods[i] == 0xffffffff)
+ continue;
+
+ build_vmodel(builder, ele->vmods[i] >> 16,
+ ele->vmods[i] & 0xffff, true, true);
+ }
+
+ l_dbus_message_builder_leave_array(builder);
+
+ return true;
+}
+
+static bool ele_idx_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_el *ele = user_data;
+
+ l_dbus_message_builder_append_basic(builder, 'y', &ele->index);
+
+ return true;
+}
+
+static struct l_dbus_message *dev_msg_recv_call(struct l_dbus *dbus,
+ struct l_dbus_message *msg,
+ void *user_data)
+{
+ struct msg_data *rsp;
+ struct l_dbus_message_iter iter;
+ uint16_t src, idx;
+ uint8_t *data;
+ uint32_t n;
+ bool rmt;
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ if (!l_dbus_message_get_arguments(msg, "qbqay", &src, &rmt, &idx,
+ &iter)) {
+ l_error("Cannot parse received message");
+ return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+ }
+
+ if (!l_dbus_message_iter_get_fixed_array(&iter, &data, &n)) {
+ l_error("Cannot parse received message: data");
+ return l_dbus_message_new_error(msg, dbus_err_args, NULL);
+ }
+
+ printf("Received dev key message (len %u):", n);
+ {
+ uint32_t i;
+
+ for (i = 0; i < n; i++)
+ printf("%x ", data[i]);
+ printf("\n");
+ }
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP) {
+ if (n == init_add_netkey_rsp.len &&
+ !memcmp(data, init_add_netkey_rsp.data, n))
+ l_idle_oneshot(init_continue, NULL, NULL);
+ else if (n == init_add_appkey_rsp.len &&
+ !memcmp(data, init_add_appkey_rsp.data, n))
+ l_idle_oneshot(init_continue, NULL, NULL);
+ else {
+ init_failed = true;
+ l_tester_pre_setup_failed(tester);
+ }
+ } else {
+ rsp = l_tester_get_data(tester);
+
+ if (rsp && rsp->len == n && !memcmp(data, rsp->data, n))
+ l_idle_oneshot(test_success, NULL, NULL);
+ else
+ l_idle_oneshot(test_fail, NULL, NULL);
+ }
+
+ return l_dbus_message_new_method_return(msg);
+}
+
+static void setup_ele_iface(struct l_dbus_interface *iface)
+{
+ /* Properties */
+ l_dbus_interface_property(iface, "Index", 0, "y", ele_idx_getter,
+ NULL);
+ l_dbus_interface_property(iface, "VendorModels", 0, "a(qqa{sv})",
+ vmod_getter, NULL);
+ l_dbus_interface_property(iface, "Models", 0, "a(qa{sv})", mod_getter,
+ NULL);
+
+ /* Methods */
+ l_dbus_interface_method(iface, "DevKeyMessageReceived", 0,
+ dev_msg_recv_call, "", "qbqay", "source",
+ "remote", "net_index", "data");
+
+ /* TODO: Other methods? */
+}
+
+static bool cid_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+
+ l_dbus_message_builder_append_basic(builder, 'q', &app->cid);
+
+ return true;
+}
+
+static bool pid_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+
+ l_dbus_message_builder_append_basic(builder, 'q', &app->pid);
+
+ return true;
+}
+
+static bool vid_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+
+ l_dbus_message_builder_append_basic(builder, 'q', &app->vid);
+
+ return true;
+}
+static bool crpl_getter(struct l_dbus *dbus,
+ struct l_dbus_message *message,
+ struct l_dbus_message_builder *builder,
+ void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+
+ l_dbus_message_builder_append_basic(builder, 'q', &app->crpl);
+
+ return true;
+}
+
+static void property_changed(struct l_dbus_proxy *proxy, const char *name,
+ struct l_dbus_message *msg, void *user_data)
+{
+ struct meshcfg_app *app = user_data;
+ struct meshcfg_node *node = app->node;
+ const char *interface = l_dbus_proxy_get_interface(proxy);
+ const char *path = l_dbus_proxy_get_path(proxy);
+
+ if (strcmp(path, node->path))
+ return;
+
+ printf("Property changed: %s %s %s\n", name, path, interface);
+
+ if (!strcmp(interface, "org.bluez.mesh.Node1")) {
+
+ if (!strcmp(name, "IvIndex")) {
+ uint32_t ivi;
+
+ if (!l_dbus_message_get_arguments(msg, "u", &ivi))
+ return;
+
+ printf("New IV Index: %u\n", ivi);
+ }
+ }
+}
+
+static void setup_app_iface(struct l_dbus_interface *iface)
+{
+ l_dbus_interface_property(iface, "CompanyID", 0, "q", cid_getter,
+ NULL);
+ l_dbus_interface_property(iface, "VersionID", 0, "q", vid_getter,
+ NULL);
+ l_dbus_interface_property(iface, "ProductID", 0, "q", pid_getter,
+ NULL);
+ l_dbus_interface_property(iface, "CRPL", 0, "q", crpl_getter, NULL);
+
+ l_dbus_interface_method(iface, "JoinComplete", 0, join_complete,
+ "", "t", "token");
+
+ /* TODO: Other methods? */
+}
+
+static bool register_app_iface(void)
+{
+ if (!l_dbus_register_interface(dbus, MESH_APPLICATION_INTERFACE,
+ setup_app_iface, NULL, false)) {
+ l_error("Failed to register interface %s",
+ MESH_APPLICATION_INTERFACE);
+ return false;
+ }
+
+ if (!l_dbus_register_interface(dbus, MESH_ELEMENT_INTERFACE,
+ setup_ele_iface, NULL, false)) {
+ l_error("Failed to register interface %s",
+ MESH_ELEMENT_INTERFACE);
+ return false;
+ }
+
+ return true;
+}
+
+static bool register_app(struct meshcfg_app *app)
+{
+ uint32_t i;
+
+ if (!l_dbus_register_object(dbus, app->path, NULL, NULL,
+ MESH_APPLICATION_INTERFACE, app,
+ NULL)) {
+ l_error("Failed to register object %s", app->path);
+ return false;
+ }
+
+ for (i = 0; i < L_ARRAY_SIZE(app->ele) && i < app->num_ele; i++) {
+ if (!l_dbus_register_object(dbus, app->ele[i].path, NULL, NULL,
+ MESH_ELEMENT_INTERFACE, &app->ele[i], NULL)) {
+ l_error("Failed to register obj %s", app->ele[i].path);
+ l_dbus_unregister_interface(dbus,
+ MESH_ELEMENT_INTERFACE);
+ return false;
+ }
+ }
+
+ if (!l_dbus_object_add_interface(dbus, app->path,
+ L_DBUS_INTERFACE_OBJECT_MANAGER, NULL)) {
+ l_error("Failed to add interface %s",
+ L_DBUS_INTERFACE_OBJECT_MANAGER);
+ return false;
+ }
+
+ return true;
+}
+
+static void client_ready(struct l_dbus_client *client, void *user_data)
+{
+ enum l_tester_stage stage = l_tester_get_stage(tester);
+
+ printf("D-Bus client ready\n");
+
+ if (!register_app_iface() || !register_app(&client_app) ||
+ !register_app(&server_app))
+ return;
+
+ if (stage == L_TESTER_STAGE_PRE_SETUP)
+ init_continue(NULL);
+}
+
+static void client_connected(struct l_dbus *dbus, void *user_data)
+{
+ printf("D-Bus client connected\n");
+}
+
+static void client_disconnected(struct l_dbus *dbus, void *user_data)
+{
+ printf("D-Bus client disconnected, exit\n");
+ l_main_exit();
+}
+
+static void ready_callback(void *user_data)
+{
+ printf("Connected to D-Bus\n");
+
+ if (l_dbus_object_manager_enable(dbus, "/"))
+ return;
+
+ printf("Failed to register the ObjectManager\n");
+ init_failed = true;
+ l_tester_pre_setup_failed(tester);
+}
+
+static void init_test(const void *data)
+{
+ if (init_failed) {
+ l_tester_pre_setup_failed(tester);
+ return;
+ }
+
+ if (init_done) {
+ l_tester_pre_setup_complete(tester);
+ return;
+ }
+
+ node_proxies = l_queue_new();
+
+ dbus = l_dbus_new_default(L_DBUS_SESSION_BUS);
+
+ l_dbus_set_ready_handler(dbus, ready_callback, NULL, NULL);
+ client = l_dbus_client_new(dbus, BLUEZ_MESH_NAME, "/org/bluez/mesh");
+
+ l_dbus_client_set_connect_handler(client, client_connected, NULL, NULL);
+ l_dbus_client_set_disconnect_handler(client, client_disconnected, NULL,
+ NULL);
+ l_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
+ property_changed, NULL, NULL);
+ l_dbus_client_set_ready_handler(client, client_ready, NULL, NULL);
+
+}
+
+static void init_startup_chain(void)
+{
+
+ startup_chain = l_queue_new();
+
+ l_queue_push_tail(startup_chain, &init_create_client);
+ l_queue_push_tail(startup_chain, &init_import_server);
+ l_queue_push_tail(startup_chain, &init_attach_client);
+ l_queue_push_tail(startup_chain, &init_import_subnet);
+ l_queue_push_tail(startup_chain, &init_import_remote);
+ l_queue_push_tail(startup_chain, &init_add_netkey);
+ l_queue_push_tail(startup_chain, &init_create_appkey);
+ l_queue_push_tail(startup_chain, &init_add_appkey);
+}
+
+static int del_fobject(const char *fpath, const struct stat *sb, int typeflag,
+ struct FTW *ftwbuf)
+{
+ switch (typeflag) {
+ case FTW_DP:
+ rmdir(fpath);
+ break;
+
+ case FTW_SL:
+ default:
+ remove(fpath);
+ break;
+ }
+
+ return 0;
+}
+
+#define tester_add_with_response(name, test_data, test_func, rsp_data) \
+ l_tester_add_full(tester, name, test_data, init_test, NULL, \
+ test_func, NULL, NULL, 2, rsp_data, NULL)
+
+static void signal_callback(unsigned int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ if (!terminated)
+ l_main_quit();
+
+ terminated = true;
+ break;
+ }
+}
+
+static void done_callback(struct l_tester *tester)
+{
+ if (terminated)
+ return;
+
+ l_main_quit();
+ terminated = true;
+}
+
+static const struct option options[] = {
+ { "version", no_argument, NULL, 'v' },
+ { "list", no_argument, NULL, 'l' },
+ { "prefix", required_argument, NULL, 'p' },
+ { "string", required_argument, NULL, 's' },
+ { }
+};
+
+static void usage(void)
+{
+ fprintf(stderr,
+ "Usage:\n"
+ "\%s [options]\n", tester_name);
+ fprintf(stderr,
+ "Options:\n"
+ "\t-v, --version Show version information and exit\n"
+ "\t-l, --list Only list the tests to be run\n"
+ "\t-p, --prefix Run tests matching the provided prefix\n"
+ "\t-s, --string Run tests matching the provided string\n");
+}
+
+static void parse_options(int *argc, char ***argv)
+{
+ tester_name = strrchr(*argv[0], '/');
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(*argc, *argv, "s:p:vl", options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'v':
+ printf("%s\n", VERSION);
+ exit(EXIT_SUCCESS);
+ case 'l':
+ option_list = true;
+ break;
+ case 'p':
+ option_prefix = optarg;
+ break;
+ case 's':
+ option_string = optarg;
+ break;
+ default:
+ usage();
+ exit(0);
+ }
+ }
+}
+
+static bool setup_test_dir(void)
+{
+ char *bluez_dir;
+ char buf[PATH_MAX];
+ ssize_t len;
+
+ len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
+
+ if (len == -1)
+ return false;
+
+ buf[len] = '\0';
+
+ test_dir = l_strdup_printf("/tmp/mesh");
+ nftw(test_dir, del_fobject, 5, FTW_DEPTH | FTW_PHYS);
+
+ if (mkdir(test_dir, 0700) != 0) {
+ printf("Failed to create dir %s\n", test_dir);
+ l_free(test_dir);
+ return false;
+ }
+
+ bluez_dir = dirname(dirname(buf));
+ exe = l_strdup_printf("%s/mesh/bluetooth-meshd", bluez_dir);
+ io = l_strdup_printf("unit:%s/%s", test_dir, "test_sk");
+
+ return true;
+}
+
+int main(int argc, char *argv[])
+{
+ int status = EXIT_SUCCESS, pid = -1;
+
+ if (!setup_test_dir())
+ return EXIT_FAILURE;
+
+ l_log_set_stderr();
+ l_main_init();
+
+ parse_options(&argc, &argv);
+
+ if (!option_list) {
+
+ pid = fork();
+ if (pid < 0)
+ return EXIT_FAILURE;
+
+ if (pid == 0) {
+ char *const dargs[] = {
+ exe,
+ "--io",
+ io,
+ "-s",
+ test_dir,
+ NULL
+ };
+
+ printf("spawning %s --io %s -s %s", exe, io, test_dir);
+ execv(exe, dargs);
+ return EXIT_SUCCESS;
+ }
+ }
+
+ tester = l_tester_new(option_prefix, option_string, option_list);
+
+ init_startup_chain();
+
+ l_tester_add_full(tester, "Config AppKey Add: Success",
+ &test_add_appkey, init_test, create_appkey, add_appkey,
+ NULL, NULL, 2, &test_add_appkey_rsp, NULL);
+
+ tester_add_with_response("Config Default TTL Set: Success",
+ &test_set_ttl_req, send_cfg_msg,
+ &test_set_ttl_rsp);
+
+ tester_add_with_response("Config Get Device Composition: Success",
+ &test_dev_comp_req, send_cfg_msg,
+ &test_dev_comp_rsp);
+
+ tester_add_with_response("Config Bind: Success",
+ &test_bind_req, send_cfg_msg,
+ &test_bind_rsp);
+
+ tester_add_with_response("Config Bind: Error Invalid Model",
+ &test_bind_inv_mod_req, send_cfg_msg,
+ &test_bind_inv_mod_rsp);
+
+ l_tester_start(tester, done_callback);
+
+ if (!option_list && !terminated) {
+ l_main_run_with_signal(signal_callback, NULL);
+
+ if (!l_tester_summarize(tester))
+ status = EXIT_FAILURE;
+ }
+
+ l_queue_destroy(startup_chain, NULL);
+ l_queue_destroy(node_proxies, NULL);
+
+ l_free(client_app.node);
+ l_free(server_app.node);
+ l_dbus_client_destroy(client);
+ l_dbus_destroy(dbus);
+
+ l_tester_destroy(tester);
+
+ if (pid > 0)
+ kill(pid, SIGTERM);
+
+ l_free(test_dir);
+ l_free(io);
+ l_free(exe);
+
+ return status;
+}
--
2.26.2

2021-03-19 20:58:40

by bluez.test.bot

[permalink] [raw]
Subject: RE: Framework for non-interactive mesh test

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=451875

---Test result---

##############################
Test: CheckPatch - PASS

##############################
Test: CheckGitLint - PASS

##############################
Test: CheckBuild - FAIL
Output:
tools/mesh-cfgtest.c: In function ‘init_continue’:
tools/mesh-cfgtest.c:356:7: error: variable ‘stage’ has initializer but incomplete type
356 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:356:30: error: implicit declaration of function ‘l_tester_get_stage’ [-Werror=implicit-function-declaration]
356 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:356:22: error: storage size of ‘stage’ isn’t known
356 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:359:15: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
359 | stage == L_TESTER_STAGE_PRE_SETUP) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:359:15: note: each undeclared identifier is reported only once for each function it appears in
tools/mesh-cfgtest.c:360:3: error: implicit declaration of function ‘l_tester_pre_setup_complete’ [-Werror=implicit-function-declaration]
360 | l_tester_pre_setup_complete(tester);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:356:22: error: unused variable ‘stage’ [-Werror=unused-variable]
356 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘test_success’:
tools/mesh-cfgtest.c:372:2: error: implicit declaration of function ‘l_tester_test_passed’ [-Werror=implicit-function-declaration]
372 | l_tester_test_passed(tester);
| ^~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c: In function ‘test_fail’:
tools/mesh-cfgtest.c:377:2: error: implicit declaration of function ‘l_tester_test_failed’ [-Werror=implicit-function-declaration]
377 | l_tester_test_failed(tester);
| ^~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c: In function ‘create_appkey_reply’:
tools/mesh-cfgtest.c:480:7: error: variable ‘stage’ has initializer but incomplete type
480 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:480:22: error: storage size of ‘stage’ isn’t known
480 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:488:16: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
488 | if (stage == L_TESTER_STAGE_PRE_SETUP) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:490:4: error: implicit declaration of function ‘l_tester_pre_setup_failed’ [-Werror=implicit-function-declaration]
490 | l_tester_pre_setup_failed(tester);
| ^~~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:492:4: error: implicit declaration of function ‘l_tester_setup_failed’ [-Werror=implicit-function-declaration]
492 | l_tester_setup_failed(tester);
| ^~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:497:4: error: implicit declaration of function ‘l_tester_setup_complete’ [-Werror=implicit-function-declaration]
497 | l_tester_setup_complete(tester);
| ^~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:480:22: error: unused variable ‘stage’ [-Werror=unused-variable]
480 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘import_remote_reply’:
tools/mesh-cfgtest.c:527:7: error: variable ‘stage’ has initializer but incomplete type
527 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:527:22: error: storage size of ‘stage’ isn’t known
527 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:535:16: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
535 | if (stage == L_TESTER_STAGE_PRE_SETUP) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:546:3: error: implicit declaration of function ‘l_tester_test_abort’ [-Werror=implicit-function-declaration]
546 | l_tester_test_abort(tester);
| ^~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:527:22: error: unused variable ‘stage’ [-Werror=unused-variable]
527 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘import_subnet_reply’:
tools/mesh-cfgtest.c:579:7: error: variable ‘stage’ has initializer but incomplete type
579 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:579:22: error: storage size of ‘stage’ isn’t known
579 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:581:15: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
581 | if (stage != L_TESTER_STAGE_PRE_SETUP)
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:591:10: error: ‘return’ with a value, in function returning void [-Werror=return-type]
591 | return l_tester_pre_setup_failed(tester);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:576:13: note: declared here
576 | static void import_subnet_reply(struct l_dbus_proxy *proxy,
| ^~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:579:22: error: unused variable ‘stage’ [-Werror=unused-variable]
579 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘attach_node_reply’:
tools/mesh-cfgtest.c:630:7: error: variable ‘stage’ has initializer but incomplete type
630 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:630:22: error: storage size of ‘stage’ isn’t known
630 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:660:15: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
660 | if (stage == L_TESTER_STAGE_PRE_SETUP)
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:630:22: error: unused variable ‘stage’ [-Werror=unused-variable]
630 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘join_complete’:
tools/mesh-cfgtest.c:703:7: error: variable ‘stage’ has initializer but incomplete type
703 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:703:22: error: storage size of ‘stage’ isn’t known
703 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:706:16: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
706 | if (stage == L_TESTER_STAGE_PRE_SETUP) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:703:22: error: unused variable ‘stage’ [-Werror=unused-variable]
703 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘dev_msg_recv_call’:
tools/mesh-cfgtest.c:967:7: error: variable ‘stage’ has initializer but incomplete type
967 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:967:22: error: storage size of ‘stage’ isn’t known
967 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:989:15: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
989 | if (stage == L_TESTER_STAGE_PRE_SETUP) {
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:1001:9: error: implicit declaration of function ‘l_tester_get_data’ [-Werror=implicit-function-declaration]
1001 | rsp = l_tester_get_data(tester);
| ^~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:1001:7: error: assignment to ‘struct msg_data *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
1001 | rsp = l_tester_get_data(tester);
| ^
tools/mesh-cfgtest.c:967:22: error: unused variable ‘stage’ [-Werror=unused-variable]
967 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘client_ready’:
tools/mesh-cfgtest.c:1171:7: error: variable ‘stage’ has initializer but incomplete type
1171 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~~~~~~~~~~
tools/mesh-cfgtest.c:1171:22: error: storage size of ‘stage’ isn’t known
1171 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c:1179:15: error: ‘L_TESTER_STAGE_PRE_SETUP’ undeclared (first use in this function)
1179 | if (stage == L_TESTER_STAGE_PRE_SETUP)
| ^~~~~~~~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:1171:22: error: unused variable ‘stage’ [-Werror=unused-variable]
1171 | enum l_tester_stage stage = l_tester_get_stage(tester);
| ^~~~~
tools/mesh-cfgtest.c: In function ‘main’:
tools/mesh-cfgtest.c:1407:11: error: implicit declaration of function ‘l_tester_new’; did you mean ‘l_tls_new’? [-Werror=implicit-function-declaration]
1407 | tester = l_tester_new(option_prefix, option_string, option_list);
| ^~~~~~~~~~~~
| l_tls_new
tools/mesh-cfgtest.c:1407:9: error: assignment to ‘struct l_tester *’ from ‘int’ makes pointer from integer without a cast [-Werror=int-conversion]
1407 | tester = l_tester_new(option_prefix, option_string, option_list);
| ^
tools/mesh-cfgtest.c:1411:2: error: implicit declaration of function ‘l_tester_add_full’ [-Werror=implicit-function-declaration]
1411 | l_tester_add_full(tester, "Config AppKey Add: Success",
| ^~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:1431:2: error: implicit declaration of function ‘l_tester_start’; did you mean ‘l_tls_start’? [-Werror=implicit-function-declaration]
1431 | l_tester_start(tester, done_callback);
| ^~~~~~~~~~~~~~
| l_tls_start
tools/mesh-cfgtest.c:1436:8: error: implicit declaration of function ‘l_tester_summarize’ [-Werror=implicit-function-declaration]
1436 | if (!l_tester_summarize(tester))
| ^~~~~~~~~~~~~~~~~~
tools/mesh-cfgtest.c:1448:2: error: implicit declaration of function ‘l_tester_destroy’; did you mean ‘l_queue_destroy’? [-Werror=implicit-function-declaration]
1448 | l_tester_destroy(tester);
| ^~~~~~~~~~~~~~~~
| l_queue_destroy
cc1: all warnings being treated as errors
make[1]: *** [Makefile:6846: tools/mesh-cfgtest.o] Error 1
make: *** [Makefile:4058: all] Error 2


##############################
Test: MakeCheck - SKIPPED
Output:
checkbuild not success



---
Regards,
Linux Bluetooth

2021-03-25 06:48:21

by bluez.test.bot

[permalink] [raw]
Subject: RE: Framework for non-interactive mesh test

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=451875

---Test result---

##############################
Test: CheckPatch - PASS

##############################
Test: CheckGitLint - PASS

##############################
Test: CheckBuild: Setup ELL - PASS

##############################
Test: CheckBuild: Setup - PASS

##############################
Test: CheckBuild - PASS

##############################
Test: MakeCheck - PASS

##############################
Test: CheckBuild w/external ell - FAIL
Output:
/usr/bin/ld: tools/mesh-cfgtest.o: in function `join_complete':
/github/workspace/src2/tools/mesh-cfgtest.c:703: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `dev_msg_recv_call':
/github/workspace/src2/tools/mesh-cfgtest.c:967: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `attach_node_reply':
/github/workspace/src2/tools/mesh-cfgtest.c:630: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `import_subnet_reply':
/github/workspace/src2/tools/mesh-cfgtest.c:579: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `create_appkey_reply':
/github/workspace/src2/tools/mesh-cfgtest.c:480: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o:/github/workspace/src2/tools/mesh-cfgtest.c:356: more undefined references to `l_tester_get_stage' follow
/usr/bin/ld: tools/mesh-cfgtest.o: in function `main':
/github/workspace/src2/tools/mesh-cfgtest.c:1411: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1415: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1419: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1423: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1427: undefined reference to `l_tester_add_full'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:5884: tools/mesh-cfgtest] Error 1
make: *** [Makefile:4058: all] Error 2




---
Regards,
Linux Bluetooth

2021-04-06 06:06:24

by bluez.test.bot

[permalink] [raw]
Subject: RE: Framework for non-interactive mesh test

This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=451875

---Test result---

##############################
Test: CheckPatch - PASS

##############################
Test: CheckGitLint - PASS

##############################
Test: CheckBuild: Setup ELL - PASS

##############################
Test: CheckBuild: Setup - PASS

##############################
Test: CheckBuild - PASS

##############################
Test: MakeCheck - PASS

##############################
Test: CheckBuild w/external ell - FAIL
Output:
/usr/bin/ld: tools/mesh-cfgtest.o: in function `join_complete':
/github/workspace/src2/tools/mesh-cfgtest.c:703: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `dev_msg_recv_call':
/github/workspace/src2/tools/mesh-cfgtest.c:967: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `attach_node_reply':
/github/workspace/src2/tools/mesh-cfgtest.c:630: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `import_subnet_reply':
/github/workspace/src2/tools/mesh-cfgtest.c:579: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o: in function `create_appkey_reply':
/github/workspace/src2/tools/mesh-cfgtest.c:480: undefined reference to `l_tester_get_stage'
/usr/bin/ld: tools/mesh-cfgtest.o:/github/workspace/src2/tools/mesh-cfgtest.c:356: more undefined references to `l_tester_get_stage' follow
/usr/bin/ld: tools/mesh-cfgtest.o: in function `main':
/github/workspace/src2/tools/mesh-cfgtest.c:1411: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1415: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1419: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1423: undefined reference to `l_tester_add_full'
/usr/bin/ld: /github/workspace/src2/tools/mesh-cfgtest.c:1427: undefined reference to `l_tester_add_full'
collect2: error: ld returned 1 exit status
make[1]: *** [Makefile:5884: tools/mesh-cfgtest] Error 1
make: *** [Makefile:4058: all] Error 2




---
Regards,
Linux Bluetooth

2021-04-10 17:35:53

by Gix, Brian

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 0/2] Framework for non-interactive mesh test

Patchset Applied.

On Fri, 2021-03-19 at 13:38 -0700, Inga Stotland wrote:
> v4: Use tester frameworks from ELL library
>
> **********
> v3: Fixed copyright style in mesh-io-unit.h
>
> **********
> v2: Fixed errors reported by bluez-bot.
> Regarding the warnings for the "__attribute__((packed))":
> keeping this style to be consistent with the rest of BLueZ codebase
>
> **********
> This patch set introduces a framework for non-interactive testing of
> mesh daemon functionality and consists of the three components:
>
> 1. A version of src/shared/tester that uses ELL primitives:
> everything mesh-related uses ELL.
>
> 2. New type of mesh IO used only for testing.
>
> 3. The mesh-cfgtest tool that excercises a number of mesh D-Bus
> API calls. Currently, the tool covers limited number of initial
> test cases. The extended coverage will be provided after this
> patch set is accepted.
>
> Brian Gix (1):
> mesh: Add unit test IO
>
> Inga Stotland (1):
> tools/mesh-cfgtest: Non-iteractive test for mesh
>
> Makefile.am | 14 +-
> Makefile.mesh | 2 +
> Makefile.tools | 6 +
> mesh/main.c | 51 +-
> mesh/mesh-io-unit.c | 533 +++++++++++++++
> mesh/mesh-io-unit.h | 11 +
> mesh/mesh-io.c | 9 +-
> mesh/mesh-io.h | 3 +-
> tools/mesh-cfgtest.c | 1458 ++++++++++++++++++++++++++++++++++++++++++
> 9 files changed, 2065 insertions(+), 22 deletions(-)
> create mode 100644 mesh/mesh-io-unit.c
> create mode 100644 mesh/mesh-io-unit.h
> create mode 100644 tools/mesh-cfgtest.c
>