2014-10-30 02:45:35

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 00/10] Unit tests for gattrib API

Since we want to replace GAttrib with a bt_att shim for a smooth transition
to bt_att profiles, this patch cleans up the API and adds unit tests for
GAttrib functions.

This adds tests for all but the register / unregister functions, which
I will send in a separate patch since they are slightly more complicated due
to catchalls.

v1 -> v2:
* Add g_attrib_register tests
* Cleanup of unit/test-gattrib.c
* Bugfix for GATTRIB_ALL_REQS

v2 -> v3:
* Just move g_attrib_is_encrypted to static (Luiz)
* Check that registered-for PDUs were delivered
* Respond to Request PDU when registered for it.
* Copy result PDUs since they aren't guaranteed to live beyond callback.

v3 -> v4:
* Rework to use g_main_loop_run() for running
* Reuse pdu_data struct in expect_response

Michael Janssen (10):
Remove unused g_attrib_set_debug function
attrib: Remove MTU-probing code
attrib: Add mtu argument to g_attrib_new
attrib: remove g_attrib_is_encrypted
Add unit tests for gattrib
attrib: Add unit tests for g_attrib_register
attrib: fix GATTRIB_ALL_REQS behavior
unit/gattrib: Check expected PDUs were delivered
unit/gattrib: Respond to the request PDU.
unit/gattrib: copy result PDU

.gitignore | 1 +
Makefile.am | 7 +
attrib/gattrib.c | 61 +++---
attrib/gattrib.h | 7 +-
attrib/gatttool.c | 17 +-
attrib/interactive.c | 17 +-
src/attrib-server.c | 12 ++
src/device.c | 11 +-
unit/test-gattrib.c | 578 +++++++++++++++++++++++++++++++++++++++++++++++++++
9 files changed, 666 insertions(+), 45 deletions(-)
create mode 100644 unit/test-gattrib.c

--
2.1.0.rc2.206.gedb03e5



2014-10-31 14:00:12

by Luiz Augusto von Dentz

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 03/10] attrib: Add mtu argument to g_attrib_new

Hi Michael,

On Thu, Oct 30, 2014 at 4:45 AM, Michael Janssen <[email protected]> wrote:
> Instead of using the default MTU, use one passed
> in by the user, and detect it from the channel when
> it is created.
> ---
> attrib/gattrib.c | 10 +++-------
> attrib/gattrib.h | 2 +-
> attrib/gatttool.c | 17 ++++++++++++++++-
> attrib/interactive.c | 17 ++++++++++++++++-
> src/device.c | 11 ++++++++++-
> 5 files changed, 46 insertions(+), 11 deletions(-)
>
> diff --git a/attrib/gattrib.c b/attrib/gattrib.c
> index fa41ade..a6511a2 100644
> --- a/attrib/gattrib.c
> +++ b/attrib/gattrib.c
> @@ -473,11 +473,9 @@ done:
> return TRUE;
> }
>
> -GAttrib *g_attrib_new(GIOChannel *io)
> +GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu)
> {
> struct _GAttrib *attrib;
> - uint16_t att_mtu;
> - uint16_t cid;
>
> g_io_channel_set_encoding(io, NULL, NULL);
> g_io_channel_set_buffered(io, FALSE);
> @@ -486,10 +484,8 @@ GAttrib *g_attrib_new(GIOChannel *io)
> if (attrib == NULL)
> return NULL;
>
> - att_mtu = ATT_DEFAULT_LE_MTU;
> -
> - attrib->buf = g_malloc0(att_mtu);
> - attrib->buflen = att_mtu;
> + attrib->buf = g_malloc0(mtu);
> + attrib->buflen = mtu;
>
> attrib->io = g_io_channel_ref(io);
> attrib->requests = g_queue_new();
> diff --git a/attrib/gattrib.h b/attrib/gattrib.h
> index 18df2ad..99b8b37 100644
> --- a/attrib/gattrib.h
> +++ b/attrib/gattrib.h
> @@ -42,7 +42,7 @@ typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
> typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
> gpointer user_data);
>
> -GAttrib *g_attrib_new(GIOChannel *io);
> +GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu);
> GAttrib *g_attrib_ref(GAttrib *attrib);
> void g_attrib_unref(GAttrib *attrib);
>
> diff --git a/attrib/gatttool.c b/attrib/gatttool.c
> index db5da62..8f92d62 100644
> --- a/attrib/gatttool.c
> +++ b/attrib/gatttool.c
> @@ -123,6 +123,9 @@ static gboolean listen_start(gpointer user_data)
> static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
> {
> GAttrib *attrib;
> + uint16_t mtu;
> + uint16_t cid;
> + GError *gerr = NULL;
>
> if (err) {
> g_printerr("%s\n", err->message);
> @@ -130,7 +133,19 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
> g_main_loop_quit(event_loop);
> }
>
> - attrib = g_attrib_new(io);
> + bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
> + BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
> +
> + if (gerr) {
> + g_printerr("Can't detect MTU, using default: %s", gerr->message);
> + g_error_free(gerr);
> + mtu = ATT_DEFAULT_LE_MTU;
> + }
> +
> + if (cid == ATT_CID)
> + mtu = ATT_DEFAULT_LE_MTU;
> +
> + attrib = g_attrib_new(io, mtu);
>
> if (opt_listen)
> g_idle_add(listen_start, attrib);
> diff --git a/attrib/interactive.c b/attrib/interactive.c
> index 08f39f7..7911ba5 100644
> --- a/attrib/interactive.c
> +++ b/attrib/interactive.c
> @@ -150,13 +150,28 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
>
> static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
> {
> + uint16_t mtu;
> + uint16_t cid;
> +
> if (err) {
> set_state(STATE_DISCONNECTED);
> error("%s\n", err->message);
> return;
> }
>
> - attrib = g_attrib_new(iochannel);
> + bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu,
> + BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
> +
> + if (err) {
> + g_printerr("Can't detect MTU, using default: %s", err->message);
> + g_error_free(err);
> + mtu = ATT_DEFAULT_LE_MTU;
> + }
> +
> + if (cid == ATT_CID)
> + mtu = ATT_DEFAULT_LE_MTU;
> +
> + attrib = g_attrib_new(iochannel, mtu);
> g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
> events_handler, attrib, NULL);
> g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
> diff --git a/src/device.c b/src/device.c
> index 875a5c5..0925951 100644
> --- a/src/device.c
> +++ b/src/device.c
> @@ -3627,11 +3627,17 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
> GError *gerr = NULL;
> GAttrib *attrib;
> BtIOSecLevel sec_level;
> + uint16_t mtu;
> + uint16_t cid;
>
> bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
> + BT_IO_OPT_IMTU, &mtu,
> + BT_IO_OPT_CID, &cid,
> BT_IO_OPT_INVALID);
> +
> if (gerr) {
> error("bt_io_get: %s", gerr->message);
> + mtu = ATT_DEFAULT_LE_MTU;
> g_error_free(gerr);
> return false;
> }
> @@ -3649,7 +3655,10 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
> }
> }
>
> - attrib = g_attrib_new(io);
> + if (cid == ATT_CID)
> + mtu = ATT_DEFAULT_LE_MTU;
> +
> + attrib = g_attrib_new(io, mtu);
> if (!attrib) {
> error("Unable to create new GAttrib instance");
> return false;
> --
> 2.1.0.rc2.206.gedb03e5

I have to stop at this one since I have made a fix to the previous
patch that did not compile cleanly this one needs rebasing now, this
actually have some other compilation problems as well so please make
sure you have everything enable, run bootstrap-configure, also you can
make sure the patches compile separately by doing: git rebase ...
--exec make


--
Luiz Augusto von Dentz

2014-10-30 22:29:41

by Arman Uguray

[permalink] [raw]
Subject: Re: [PATCH BlueZ v4 00/10] Unit tests for gattrib API

> On Wed, Oct 29, 2014 at 7:45 PM, Michael Janssen <[email protected]> wrote:
> Since we want to replace GAttrib with a bt_att shim for a smooth transition
> to bt_att profiles, this patch cleans up the API and adds unit tests for
> GAttrib functions.
>
> This adds tests for all but the register / unregister functions, which
> I will send in a separate patch since they are slightly more complicated due
> to catchalls.
>
> v1 -> v2:
> * Add g_attrib_register tests
> * Cleanup of unit/test-gattrib.c
> * Bugfix for GATTRIB_ALL_REQS
>
> v2 -> v3:
> * Just move g_attrib_is_encrypted to static (Luiz)
> * Check that registered-for PDUs were delivered
> * Respond to Request PDU when registered for it.
> * Copy result PDUs since they aren't guaranteed to live beyond callback.
>
> v3 -> v4:
> * Rework to use g_main_loop_run() for running
> * Reuse pdu_data struct in expect_response
>
> Michael Janssen (10):
> Remove unused g_attrib_set_debug function
> attrib: Remove MTU-probing code
> attrib: Add mtu argument to g_attrib_new
> attrib: remove g_attrib_is_encrypted
> Add unit tests for gattrib
> attrib: Add unit tests for g_attrib_register
> attrib: fix GATTRIB_ALL_REQS behavior
> unit/gattrib: Check expected PDUs were delivered
> unit/gattrib: Respond to the request PDU.
> unit/gattrib: copy result PDU
>
> .gitignore | 1 +
> Makefile.am | 7 +
> attrib/gattrib.c | 61 +++---
> attrib/gattrib.h | 7 +-
> attrib/gatttool.c | 17 +-
> attrib/interactive.c | 17 +-
> src/attrib-server.c | 12 ++
> src/device.c | 11 +-
> unit/test-gattrib.c | 578 +++++++++++++++++++++++++++++++++++++++++++++++++++
> 9 files changed, 666 insertions(+), 45 deletions(-)
> create mode 100644 unit/test-gattrib.c
>
> --
> 2.1.0.rc2.206.gedb03e5
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

ping

2014-10-30 02:45:36

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 01/10] Remove unused g_attrib_set_debug function

This function is not used, and also not implemented.
---
attrib/gattrib.c | 5 -----
attrib/gattrib.h | 3 ---
2 files changed, 8 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 912dffb..71d1cef 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -651,11 +651,6 @@ gboolean g_attrib_cancel_all(GAttrib *attrib)
return ret;
}

-gboolean g_attrib_set_debug(GAttrib *attrib,
- GAttribDebugFunc func, gpointer user_data)
-{
- return TRUE;
-}

uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
{
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 3fe92c7..18df2ad 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -58,9 +58,6 @@ guint g_attrib_send(GAttrib *attrib, guint id, const guint8 *pdu, guint16 len,
gboolean g_attrib_cancel(GAttrib *attrib, guint id);
gboolean g_attrib_cancel_all(GAttrib *attrib);

-gboolean g_attrib_set_debug(GAttrib *attrib,
- GAttribDebugFunc func, gpointer user_data);
-
guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
GAttribNotifyFunc func, gpointer user_data,
GDestroyNotify notify);
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:42

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 07/10] attrib: fix GATTRIB_ALL_REQS behavior

g_attrib_register(..., GATTRIB_ALL_REQS, ...) behavior would previously
include indications, this fixes it to only include requests and
commands.
---
attrib/gattrib.c | 23 ++++++++++++++++++++++-
1 file changed, 22 insertions(+), 1 deletion(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index a65d1ca..f678435 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -147,6 +147,27 @@ static bool is_response(guint8 opcode)
return false;
}

+static bool is_request(guint8 opcode)
+{
+ switch (opcode) {
+ case ATT_OP_MTU_REQ:
+ case ATT_OP_FIND_INFO_REQ:
+ case ATT_OP_FIND_BY_TYPE_REQ:
+ case ATT_OP_READ_BY_TYPE_REQ:
+ case ATT_OP_READ_REQ:
+ case ATT_OP_READ_BLOB_REQ:
+ case ATT_OP_READ_MULTI_REQ:
+ case ATT_OP_READ_BY_GROUP_REQ:
+ case ATT_OP_WRITE_REQ:
+ case ATT_OP_WRITE_CMD:
+ case ATT_OP_PREP_WRITE_REQ:
+ case ATT_OP_EXEC_WRITE_REQ:
+ return true;
+ }
+
+ return false;
+}
+
GAttrib *g_attrib_ref(GAttrib *attrib)
{
int refs;
@@ -373,7 +394,7 @@ static bool match_event(struct event *evt, const uint8_t *pdu, gsize len)
if (evt->expected == GATTRIB_ALL_EVENTS)
return true;

- if (!is_response(pdu[0]) && evt->expected == GATTRIB_ALL_REQS)
+ if (is_request(pdu[0]) && evt->expected == GATTRIB_ALL_REQS)
return true;

if (evt->expected == pdu[0] && evt->handle == GATTRIB_ALL_HANDLES)
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:44

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 09/10] unit/gattrib: Respond to the request PDU.

---
unit/test-gattrib.c | 35 ++++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)

diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
index 85f0f2b..4f1343b 100644
--- a/unit/test-gattrib.c
+++ b/unit/test-gattrib.c
@@ -357,7 +357,7 @@ static void send_test_pdus(gpointer context, struct test_pdu *pdus)
for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++) {
if (g_test_verbose())
util_hexdump('>', cur_pdu->data, cur_pdu->size,
- test_debug, "send_test_pdus: ");
+ test_debug, "send_test_pdus: ");
len = write(fd, cur_pdu->data, cur_pdu->size);
g_assert_cmpint(len, ==, cur_pdu->size);
cur_pdu->sent = true;
@@ -369,13 +369,20 @@ static void send_test_pdus(gpointer context, struct test_pdu *pdus)

#define PDU_MTU_RESP pdu(ATT_OP_MTU_RESP, 0x17)
#define PDU_FIND_INFO_REQ pdu(ATT_OP_FIND_INFO_REQ, 0x01, 0x00, 0xFF, 0xFF)
+#define PDU_NO_ATT_ERR pdu(ATT_OP_ERROR, ATT_OP_FIND_INFO_REQ, 0x00, 0x00, 0x0A)
#define PDU_IND_NODATA pdu(ATT_OP_HANDLE_IND, 0x01, 0x00)
#define PDU_INVALID_IND pdu(ATT_OP_HANDLE_IND, 0x14)
#define PDU_IND_DATA pdu(ATT_OP_HANDLE_IND, 0x14, 0x00, 0x01)

+struct expect_test_data {
+ struct test_pdu *expected;
+ GAttrib *att;
+};
+
static void notify_canary_expect(const guint8 *pdu, guint16 len, gpointer data)
{
- struct test_pdu *expected = data;
+ struct expect_test_data *expect = data;
+ struct test_pdu *expected = expect->expected;
int cmp;

if (g_test_verbose())
@@ -398,6 +405,14 @@ static void notify_canary_expect(const guint8 *pdu, guint16 len, gpointer data)
g_assert(cmp == 0);

expected->received = true;
+
+ if (pdu[0] == ATT_OP_FIND_INFO_REQ) {
+ struct test_pdu no_attributes = PDU_NO_ATT_ERR;
+ int reqid;
+ reqid = g_attrib_send(expect->att, 0, no_attributes.data,
+ no_attributes.size, NULL, NULL, NULL);
+ g_assert(reqid != 0);
+ }
}

static void test_register(struct context *cxt, gconstpointer user_data)
@@ -434,15 +449,19 @@ static void test_register(struct context *cxt, gconstpointer user_data)
};
struct test_pdu followed_ind_pdus[] = { PDU_IND_DATA, { } };
struct test_pdu *current_pdu;
+ struct expect_test_data expect;
+
+ expect.att = cxt->att;

/*
* Without registering anything, should be able to ignore everything but
* an unexpected response. */
send_test_pdus(cxt, pdus + 1);

+ expect.expected = pdus;
reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_EVENTS,
GATTRIB_ALL_HANDLES, notify_canary_expect,
- pdus, NULL);
+ &expect, NULL);

send_test_pdus(cxt, pdus);

@@ -456,9 +475,10 @@ static void test_register(struct context *cxt, gconstpointer user_data)
if (g_test_verbose())
g_print("ALL_REQS, ALL_HANDLES\r\n");

+ expect.expected = req_pdus;
reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_REQS,
GATTRIB_ALL_HANDLES, notify_canary_expect,
- req_pdus, NULL);
+ &expect, NULL);

send_test_pdus(cxt, pdus);

@@ -472,9 +492,10 @@ static void test_register(struct context *cxt, gconstpointer user_data)
if (g_test_verbose())
g_print("IND, ALL_HANDLES\r\n");

+ expect.expected = all_ind_pdus;
reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND,
GATTRIB_ALL_HANDLES, notify_canary_expect,
- all_ind_pdus, NULL);
+ &expect, NULL);

send_test_pdus(cxt, pdus);

@@ -488,9 +509,9 @@ static void test_register(struct context *cxt, gconstpointer user_data)
if (g_test_verbose())
g_print("IND, 0x0014\r\n");

+ expect.expected = followed_ind_pdus;
reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND, 0x0014,
- notify_canary_expect, followed_ind_pdus,
- NULL);
+ notify_canary_expect, &expect, NULL);

send_test_pdus(cxt, pdus);

--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:43

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 08/10] unit/gattrib: Check expected PDUs were delivered

Check that the PDUs that we expect to receive when
registering for events have been delivered.
---
unit/test-gattrib.c | 35 ++++++++++++++++++++++++++++-------
1 file changed, 28 insertions(+), 7 deletions(-)

diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
index e25746c..85f0f2b 100644
--- a/unit/test-gattrib.c
+++ b/unit/test-gattrib.c
@@ -403,7 +403,7 @@ static void notify_canary_expect(const guint8 *pdu, guint16 len, gpointer data)
static void test_register(struct context *cxt, gconstpointer user_data)
{
guint reg_id;
- gboolean success;
+ gboolean canceled;
struct test_pdu pdus[] = {
/* Unmatched by any (GATTRIB_ALL_EVENTS) */
PDU_MTU_RESP,
@@ -433,6 +433,7 @@ static void test_register(struct context *cxt, gconstpointer user_data)
{ },
};
struct test_pdu followed_ind_pdus[] = { PDU_IND_DATA, { } };
+ struct test_pdu *current_pdu;

/*
* Without registering anything, should be able to ignore everything but
@@ -445,7 +446,12 @@ static void test_register(struct context *cxt, gconstpointer user_data)

send_test_pdus(cxt, pdus);

- g_attrib_unregister(cxt->att, reg_id);
+ canceled = g_attrib_unregister(cxt->att, reg_id);
+
+ g_assert(canceled);
+
+ for (current_pdu = pdus; current_pdu->valid; current_pdu++)
+ g_assert(current_pdu->received);

if (g_test_verbose())
g_print("ALL_REQS, ALL_HANDLES\r\n");
@@ -456,7 +462,12 @@ static void test_register(struct context *cxt, gconstpointer user_data)

send_test_pdus(cxt, pdus);

- g_attrib_unregister(cxt->att, reg_id);
+ canceled = g_attrib_unregister(cxt->att, reg_id);
+
+ g_assert(canceled);
+
+ for (current_pdu = req_pdus; current_pdu->valid; current_pdu++)
+ g_assert(current_pdu->received);

if (g_test_verbose())
g_print("IND, ALL_HANDLES\r\n");
@@ -467,7 +478,12 @@ static void test_register(struct context *cxt, gconstpointer user_data)

send_test_pdus(cxt, pdus);

- g_attrib_unregister(cxt->att, reg_id);
+ canceled = g_attrib_unregister(cxt->att, reg_id);
+
+ g_assert(canceled);
+
+ for (current_pdu = all_ind_pdus; current_pdu->valid; current_pdu++)
+ g_assert(current_pdu->received);

if (g_test_verbose())
g_print("IND, 0x0014\r\n");
@@ -478,11 +494,16 @@ static void test_register(struct context *cxt, gconstpointer user_data)

send_test_pdus(cxt, pdus);

- g_attrib_unregister(cxt->att, reg_id);
+ canceled = g_attrib_unregister(cxt->att, reg_id);
+
+ g_assert(canceled);

- success = g_attrib_unregister(cxt->att, reg_id);
+ for (current_pdu = followed_ind_pdus; current_pdu->valid; current_pdu++)
+ g_assert(current_pdu->received);

- g_assert(!success);
+ canceled = g_attrib_unregister(cxt->att, reg_id);
+
+ g_assert(!canceled);
}

static void test_buffers(struct context *cxt, gconstpointer unused)
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:41

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 06/10] attrib: Add unit tests for g_attrib_register

---
unit/test-gattrib.c | 157 +++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 156 insertions(+), 1 deletion(-)

diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
index a8ff7da..e25746c 100644
--- a/unit/test-gattrib.c
+++ b/unit/test-gattrib.c
@@ -45,6 +45,23 @@

#define data(args...) ((const unsigned char[]) { args })

+struct test_pdu {
+ bool valid;
+ bool sent;
+ bool received;
+ const uint8_t *data;
+ size_t size;
+};
+
+#define pdu(args...) \
+ { \
+ .valid = true, \
+ .sent = false, \
+ .received = false, \
+ .data = data(args), \
+ .size = sizeof(data(args)), \
+ }
+
struct context {
GMainLoop *main_loop;
GIOChannel *att_io;
@@ -325,9 +342,147 @@ static void test_cancel(struct context *cxt, gconstpointer unused)
g_assert(!canceled);
}

+static void send_test_pdus(gpointer context, struct test_pdu *pdus)
+{
+ struct context *cxt = context;
+ size_t len;
+ int fd;
+ struct test_pdu *cur_pdu;
+
+ fd = g_io_channel_unix_get_fd(cxt->server_io);
+
+ for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++)
+ cur_pdu->sent = false;
+
+ for (cur_pdu = pdus; cur_pdu->valid; cur_pdu++) {
+ if (g_test_verbose())
+ util_hexdump('>', cur_pdu->data, cur_pdu->size,
+ test_debug, "send_test_pdus: ");
+ len = write(fd, cur_pdu->data, cur_pdu->size);
+ g_assert_cmpint(len, ==, cur_pdu->size);
+ cur_pdu->sent = true;
+ }
+
+ g_idle_add(context_stop_main_loop, cxt);
+ g_main_loop_run(cxt->main_loop);
+}
+
+#define PDU_MTU_RESP pdu(ATT_OP_MTU_RESP, 0x17)
+#define PDU_FIND_INFO_REQ pdu(ATT_OP_FIND_INFO_REQ, 0x01, 0x00, 0xFF, 0xFF)
+#define PDU_IND_NODATA pdu(ATT_OP_HANDLE_IND, 0x01, 0x00)
+#define PDU_INVALID_IND pdu(ATT_OP_HANDLE_IND, 0x14)
+#define PDU_IND_DATA pdu(ATT_OP_HANDLE_IND, 0x14, 0x00, 0x01)
+
+static void notify_canary_expect(const guint8 *pdu, guint16 len, gpointer data)
+{
+ struct test_pdu *expected = data;
+ int cmp;
+
+ if (g_test_verbose())
+ util_hexdump('<', pdu, len, test_debug,
+ "notify_canary_expect: ");
+
+ while (expected->valid && expected->received)
+ expected++;
+
+ g_assert(expected->valid);
+
+ if (g_test_verbose())
+ util_hexdump('?', expected->data, expected->size, test_debug,
+ "notify_canary_expect: ");
+
+ g_assert_cmpint(expected->size, ==, len);
+
+ cmp = memcmp(pdu, expected->data, expected->size);
+
+ g_assert(cmp == 0);
+
+ expected->received = true;
+}
+
static void test_register(struct context *cxt, gconstpointer user_data)
{
- /* TODO */
+ guint reg_id;
+ gboolean success;
+ struct test_pdu pdus[] = {
+ /* Unmatched by any (GATTRIB_ALL_EVENTS) */
+ PDU_MTU_RESP,
+ /*
+ * Unmatched PDU opcode
+ * Unmatched handle (GATTRIB_ALL_REQS) */
+ PDU_FIND_INFO_REQ,
+ /*
+ * Matched PDU opcode
+ * Unmatched handle (GATTRIB_ALL_HANDLES) */
+ PDU_IND_NODATA,
+ /*
+ * Matched PDU opcode
+ * Invalid length? */
+ PDU_INVALID_IND,
+ /*
+ * Matched PDU opcode
+ * Matched handle */
+ PDU_IND_DATA,
+ { },
+ };
+ struct test_pdu req_pdus[] = { PDU_FIND_INFO_REQ, { } };
+ struct test_pdu all_ind_pdus[] = {
+ PDU_IND_NODATA,
+ PDU_INVALID_IND,
+ PDU_IND_DATA,
+ { },
+ };
+ struct test_pdu followed_ind_pdus[] = { PDU_IND_DATA, { } };
+
+ /*
+ * Without registering anything, should be able to ignore everything but
+ * an unexpected response. */
+ send_test_pdus(cxt, pdus + 1);
+
+ reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_EVENTS,
+ GATTRIB_ALL_HANDLES, notify_canary_expect,
+ pdus, NULL);
+
+ send_test_pdus(cxt, pdus);
+
+ g_attrib_unregister(cxt->att, reg_id);
+
+ if (g_test_verbose())
+ g_print("ALL_REQS, ALL_HANDLES\r\n");
+
+ reg_id = g_attrib_register(cxt->att, GATTRIB_ALL_REQS,
+ GATTRIB_ALL_HANDLES, notify_canary_expect,
+ req_pdus, NULL);
+
+ send_test_pdus(cxt, pdus);
+
+ g_attrib_unregister(cxt->att, reg_id);
+
+ if (g_test_verbose())
+ g_print("IND, ALL_HANDLES\r\n");
+
+ reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND,
+ GATTRIB_ALL_HANDLES, notify_canary_expect,
+ all_ind_pdus, NULL);
+
+ send_test_pdus(cxt, pdus);
+
+ g_attrib_unregister(cxt->att, reg_id);
+
+ if (g_test_verbose())
+ g_print("IND, 0x0014\r\n");
+
+ reg_id = g_attrib_register(cxt->att, ATT_OP_HANDLE_IND, 0x0014,
+ notify_canary_expect, followed_ind_pdus,
+ NULL);
+
+ send_test_pdus(cxt, pdus);
+
+ g_attrib_unregister(cxt->att, reg_id);
+
+ success = g_attrib_unregister(cxt->att, reg_id);
+
+ g_assert(!success);
}

static void test_buffers(struct context *cxt, gconstpointer unused)
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:38

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 03/10] attrib: Add mtu argument to g_attrib_new

Instead of using the default MTU, use one passed
in by the user, and detect it from the channel when
it is created.
---
attrib/gattrib.c | 10 +++-------
attrib/gattrib.h | 2 +-
attrib/gatttool.c | 17 ++++++++++++++++-
attrib/interactive.c | 17 ++++++++++++++++-
src/device.c | 11 ++++++++++-
5 files changed, 46 insertions(+), 11 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index fa41ade..a6511a2 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -473,11 +473,9 @@ done:
return TRUE;
}

-GAttrib *g_attrib_new(GIOChannel *io)
+GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu)
{
struct _GAttrib *attrib;
- uint16_t att_mtu;
- uint16_t cid;

g_io_channel_set_encoding(io, NULL, NULL);
g_io_channel_set_buffered(io, FALSE);
@@ -486,10 +484,8 @@ GAttrib *g_attrib_new(GIOChannel *io)
if (attrib == NULL)
return NULL;

- att_mtu = ATT_DEFAULT_LE_MTU;
-
- attrib->buf = g_malloc0(att_mtu);
- attrib->buflen = att_mtu;
+ attrib->buf = g_malloc0(mtu);
+ attrib->buflen = mtu;

attrib->io = g_io_channel_ref(io);
attrib->requests = g_queue_new();
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 18df2ad..99b8b37 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -42,7 +42,7 @@ typedef void (*GAttribDebugFunc)(const char *str, gpointer user_data);
typedef void (*GAttribNotifyFunc)(const guint8 *pdu, guint16 len,
gpointer user_data);

-GAttrib *g_attrib_new(GIOChannel *io);
+GAttrib *g_attrib_new(GIOChannel *io, guint16 mtu);
GAttrib *g_attrib_ref(GAttrib *attrib);
void g_attrib_unref(GAttrib *attrib);

diff --git a/attrib/gatttool.c b/attrib/gatttool.c
index db5da62..8f92d62 100644
--- a/attrib/gatttool.c
+++ b/attrib/gatttool.c
@@ -123,6 +123,9 @@ static gboolean listen_start(gpointer user_data)
static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
GAttrib *attrib;
+ uint16_t mtu;
+ uint16_t cid;
+ GError *gerr = NULL;

if (err) {
g_printerr("%s\n", err->message);
@@ -130,7 +133,19 @@ static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
g_main_loop_quit(event_loop);
}

- attrib = g_attrib_new(io);
+ bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu,
+ BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ g_printerr("Can't detect MTU, using default: %s", gerr->message);
+ g_error_free(gerr);
+ mtu = ATT_DEFAULT_LE_MTU;
+ }
+
+ if (cid == ATT_CID)
+ mtu = ATT_DEFAULT_LE_MTU;
+
+ attrib = g_attrib_new(io, mtu);

if (opt_listen)
g_idle_add(listen_start, attrib);
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 08f39f7..7911ba5 100644
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -150,13 +150,28 @@ static void events_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)

static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
{
+ uint16_t mtu;
+ uint16_t cid;
+
if (err) {
set_state(STATE_DISCONNECTED);
error("%s\n", err->message);
return;
}

- attrib = g_attrib_new(iochannel);
+ bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu,
+ BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
+
+ if (err) {
+ g_printerr("Can't detect MTU, using default: %s", err->message);
+ g_error_free(err);
+ mtu = ATT_DEFAULT_LE_MTU;
+ }
+
+ if (cid == ATT_CID)
+ mtu = ATT_DEFAULT_LE_MTU;
+
+ attrib = g_attrib_new(iochannel, mtu);
g_attrib_register(attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
events_handler, attrib, NULL);
g_attrib_register(attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
diff --git a/src/device.c b/src/device.c
index 875a5c5..0925951 100644
--- a/src/device.c
+++ b/src/device.c
@@ -3627,11 +3627,17 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
GError *gerr = NULL;
GAttrib *attrib;
BtIOSecLevel sec_level;
+ uint16_t mtu;
+ uint16_t cid;

bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_IMTU, &mtu,
+ BT_IO_OPT_CID, &cid,
BT_IO_OPT_INVALID);
+
if (gerr) {
error("bt_io_get: %s", gerr->message);
+ mtu = ATT_DEFAULT_LE_MTU;
g_error_free(gerr);
return false;
}
@@ -3649,7 +3655,10 @@ bool device_attach_attrib(struct btd_device *dev, GIOChannel *io)
}
}

- attrib = g_attrib_new(io);
+ if (cid == ATT_CID)
+ mtu = ATT_DEFAULT_LE_MTU;
+
+ attrib = g_attrib_new(io, mtu);
if (!attrib) {
error("Unable to create new GAttrib instance");
return false;
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:37

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 02/10] attrib: Remove MTU-probing code

Probing for the MTU using bt_io is problematic for
testing because you cannot impersonate AF_BLUETOOTH sockets
with a socketpair.
---
attrib/gattrib.c | 13 +------------
1 file changed, 1 insertion(+), 12 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 71d1cef..fa41ade 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -476,27 +476,17 @@ done:
GAttrib *g_attrib_new(GIOChannel *io)
{
struct _GAttrib *attrib;
- uint16_t imtu;
uint16_t att_mtu;
uint16_t cid;
- GError *gerr = NULL;

g_io_channel_set_encoding(io, NULL, NULL);
g_io_channel_set_buffered(io, FALSE);

- bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu,
- BT_IO_OPT_CID, &cid, BT_IO_OPT_INVALID);
- if (gerr) {
- error("%s", gerr->message);
- g_error_free(gerr);
- return NULL;
- }
-
attrib = g_try_new0(struct _GAttrib, 1);
if (attrib == NULL)
return NULL;

- att_mtu = (cid == ATT_CID) ? ATT_DEFAULT_LE_MTU : imtu;
+ att_mtu = ATT_DEFAULT_LE_MTU;

attrib->buf = g_malloc0(att_mtu);
attrib->buflen = att_mtu;
@@ -651,7 +641,6 @@ gboolean g_attrib_cancel_all(GAttrib *attrib)
return ret;
}

-
uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
{
if (len == NULL)
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:45

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 10/10] unit/gattrib: copy result PDU

Memory is not guaranteed to stay around after the result
function is called, so we must copy the PDU.
---
unit/test-gattrib.c | 8 ++++++--
1 file changed, 6 insertions(+), 2 deletions(-)

diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
index 4f1343b..56dc709 100644
--- a/unit/test-gattrib.c
+++ b/unit/test-gattrib.c
@@ -212,7 +212,7 @@ static gboolean test_client(GIOChannel *channel, GIOCondition cond,

struct result_data {
guint8 status;
- const guint8 *pdu;
+ guint8 *pdu;
guint16 len;
GSourceFunc complete_cb;
gpointer user_data;
@@ -222,8 +222,10 @@ static void result_canary(guint8 status, const guint8 *pdu, guint16 len,
gpointer data)
{
struct result_data *result = data;
+
result->status = status;
- result->pdu = pdu;
+ result->pdu = g_malloc0(len);
+ memcpy(result->pdu, pdu, len);
result->len = len;

if (g_test_verbose())
@@ -267,6 +269,8 @@ static void test_send(struct context *cxt, gconstpointer unused)

cmp = memcmp(results.pdu, data.respond.data, results.len);

+ g_free(results.pdu);
+
g_assert(cmp == 0);

g_free(results.pdu);
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:40

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 05/10] Add unit tests for gattrib

This will ensure that the API behavior of gattrib is preserved.
---
.gitignore | 1 +
Makefile.am | 7 +
unit/test-gattrib.c | 377 ++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 385 insertions(+)
create mode 100644 unit/test-gattrib.c

diff --git a/.gitignore b/.gitignore
index 97daa9f..164cc97 100644
--- a/.gitignore
+++ b/.gitignore
@@ -121,6 +121,7 @@ unit/test-avdtp
unit/test-avctp
unit/test-avrcp
unit/test-gatt
+unit/test-gattrib
unit/test-*.log
unit/test-*.trs

diff --git a/Makefile.am b/Makefile.am
index 2dfea28..3fddb80 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -377,6 +377,13 @@ unit_test_gatt_SOURCES = unit/test-gatt.c
unit_test_gatt_LDADD = src/libshared-glib.la \
lib/libbluetooth-internal.la @GLIB_LIBS@

+unit_tests += unit/test-gattrib
+
+unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
+unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
+ src/libshared-glib.la \
+ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+
if MAINTAINER_MODE
noinst_PROGRAMS += $(unit_tests)
endif
diff --git a/unit/test-gattrib.c b/unit/test-gattrib.c
new file mode 100644
index 0000000..a8ff7da
--- /dev/null
+++ b/unit/test-gattrib.c
@@ -0,0 +1,377 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <inttypes.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "lib/uuid.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "src/log.h"
+
+#define DEFAULT_MTU 23
+
+#define data(args...) ((const unsigned char[]) { args })
+
+struct context {
+ GMainLoop *main_loop;
+ GIOChannel *att_io;
+ GIOChannel *server_io;
+ GAttrib *att;
+};
+
+static void setup_context(struct context *cxt, gconstpointer data)
+{
+ int err, sv[2];
+
+ cxt->main_loop = g_main_loop_new(NULL, FALSE);
+ g_assert(cxt->main_loop != NULL);
+
+ err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+ g_assert(err == 0);
+
+ cxt->att_io = g_io_channel_unix_new(sv[0]);
+ g_assert(cxt->att_io != NULL);
+
+ g_io_channel_set_close_on_unref(cxt->att_io, TRUE);
+
+ cxt->server_io = g_io_channel_unix_new(sv[1]);
+ g_assert(cxt->server_io != NULL);
+
+ g_io_channel_set_close_on_unref(cxt->server_io, TRUE);
+ g_io_channel_set_encoding(cxt->server_io, NULL, NULL);
+ g_io_channel_set_buffered(cxt->server_io, FALSE);
+
+ cxt->att = g_attrib_new(cxt->att_io, DEFAULT_MTU);
+ g_assert(cxt->att != NULL);
+}
+
+static void teardown_context(struct context *cxt, gconstpointer data)
+{
+ if (cxt->att)
+ g_attrib_unref(cxt->att);
+
+ g_io_channel_unref(cxt->server_io);
+
+ g_io_channel_unref(cxt->att_io);
+
+ g_main_loop_unref(cxt->main_loop);
+}
+
+
+static void test_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ g_print("%s%s\n", prefix, str);
+}
+
+static void destroy_canary_increment(gpointer data)
+{
+ int *canary = data;
+ (*canary)++;
+}
+
+static void test_refcount(struct context *cxt, gconstpointer unused)
+{
+ GAttrib *extra_ref;
+ int destroy_canary;
+
+ g_attrib_set_destroy_function(cxt->att, destroy_canary_increment,
+ &destroy_canary);
+
+ extra_ref = g_attrib_ref(cxt->att);
+
+ g_assert(extra_ref == cxt->att);
+
+ g_assert(destroy_canary == 0);
+
+ g_attrib_unref(extra_ref);
+
+ g_assert(destroy_canary == 0);
+
+ g_attrib_unref(cxt->att);
+
+ g_assert(destroy_canary == 1);
+
+ /* Avoid a double-free from the teardown function */
+ cxt->att = NULL;
+}
+
+static void test_get_channel(struct context *cxt, gconstpointer unused)
+{
+ GIOChannel *chan;
+
+ chan = g_attrib_get_channel(cxt->att);
+
+ g_assert(chan == cxt->att_io);
+}
+
+struct expect_response {
+ struct test_pdu expect;
+ struct test_pdu respond;
+ GSourceFunc receive_cb;
+ gpointer user_data;
+};
+
+static gboolean test_client(GIOChannel *channel, GIOCondition cond,
+ gpointer data)
+{
+ struct expect_response *cr = data;
+ int fd;
+ uint8_t buf[256];
+ ssize_t len;
+ int cmp;
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ len = read(fd, buf, sizeof(buf));
+
+ g_assert(len > 0);
+ g_assert_cmpint(len, ==, cr->expect.size);
+
+ if (g_test_verbose())
+ util_hexdump('?', cr->expect.data, cr->expect.size,
+ test_debug, "test_client: ");
+
+ cmp = memcmp(cr->expect.data, buf, len);
+
+ g_assert(cmp == 0);
+
+ cr->expect.received = true;
+
+ if (cr->receive_cb != NULL)
+ cr->receive_cb(cr->user_data);
+
+ if (cr->respond.valid) {
+ if (g_test_verbose())
+ util_hexdump('<', cr->respond.data, cr->respond.size,
+ test_debug, "test_client: ");
+ len = write(fd, cr->respond.data, cr->respond.size);
+
+ g_assert_cmpint(len, ==, cr->respond.size);
+
+ cr->respond.sent = true;
+ }
+
+ return TRUE;
+}
+
+struct result_data {
+ guint8 status;
+ const guint8 *pdu;
+ guint16 len;
+ GSourceFunc complete_cb;
+ gpointer user_data;
+};
+
+static void result_canary(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer data)
+{
+ struct result_data *result = data;
+ result->status = status;
+ result->pdu = pdu;
+ result->len = len;
+
+ if (g_test_verbose())
+ util_hexdump('<', pdu, len, test_debug, "result_canary: ");
+
+ if (result->complete_cb != NULL)
+ result->complete_cb(result->user_data);
+}
+
+static gboolean context_stop_main_loop(gpointer user_data) {
+ struct context *cxt = user_data;
+ g_main_loop_quit(cxt->main_loop);
+ return FALSE;
+}
+
+static void test_send(struct context *cxt, gconstpointer unused)
+{
+ int cmp;
+ struct result_data results;
+ struct expect_response data = {
+ .expect = pdu(0x02, 0x00, 0x02),
+ .respond = pdu(0x03, 0x02, 0x03, 0x04),
+ .receive_cb = NULL,
+ .user_data = NULL,
+ };
+
+ g_io_add_watch(cxt->server_io, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ test_client, &data);
+
+ results.complete_cb = context_stop_main_loop;
+ results.user_data = cxt;
+
+ g_attrib_send(cxt->att, 0, data.expect.data, data.expect.size,
+ result_canary, (gpointer) &results, NULL);
+
+ g_main_loop_run(cxt->main_loop);
+
+ g_assert(results.pdu != NULL);
+
+ g_assert_cmpint(results.len, ==, data.respond.size);
+
+ cmp = memcmp(results.pdu, data.respond.data, results.len);
+
+ g_assert(cmp == 0);
+
+ g_free(results.pdu);
+}
+
+struct event_info {
+ struct context *context;
+ int event_id;
+};
+
+static gboolean cancel_existing_attrib_event(gpointer user_data) {
+ struct event_info *info = user_data;
+ gboolean canceled;
+
+ canceled = g_attrib_cancel(info->context->att, info->event_id);
+
+ g_assert(canceled);
+
+ g_idle_add(context_stop_main_loop, info->context);
+
+ return FALSE;
+}
+
+static void test_cancel(struct context *cxt, gconstpointer unused)
+{
+ gboolean canceled;
+ struct result_data results;
+ struct event_info info;
+ struct expect_response data = {
+ .expect = pdu(0x02, 0x00, 0x02),
+ .respond = pdu(0x03, 0x02, 0x03, 0x04),
+ };
+
+ g_io_add_watch(cxt->server_io,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ test_client, &data);
+
+ results.pdu = NULL;
+
+ info.context = cxt;
+ info.event_id = g_attrib_send(cxt->att, 0, data.expect.data,
+ data.expect.size, result_canary,
+ &results, NULL);
+
+ data.receive_cb = cancel_existing_attrib_event;
+ data.user_data = &info;
+
+ g_main_loop_run(cxt->main_loop);
+
+ g_assert(results.pdu == NULL);
+
+ results.pdu = NULL;
+ data.expect.received = false;
+ data.respond.sent = false;
+
+ info.event_id = g_attrib_send(cxt->att, 0, data.expect.data,
+ data.expect.size, result_canary,
+ &results, NULL);
+
+ canceled = g_attrib_cancel(cxt->att, info.event_id);
+ g_assert(canceled);
+
+ g_idle_add(context_stop_main_loop, info.context);
+
+ g_main_loop_run(cxt->main_loop);
+
+ g_assert(!data.expect.received);
+ g_assert(!data.respond.sent);
+ g_assert(results.pdu == NULL);
+
+ /* Invalid ID */
+ canceled = g_attrib_cancel(cxt->att, 42);
+ g_assert(!canceled);
+}
+
+static void test_register(struct context *cxt, gconstpointer user_data)
+{
+ /* TODO */
+}
+
+static void test_buffers(struct context *cxt, gconstpointer unused)
+{
+ size_t buflen;
+ uint8_t *buf;
+ gboolean success;
+
+ buf = g_attrib_get_buffer(cxt->att, &buflen);
+ g_assert(buf != 0);
+ g_assert_cmpint(buflen, ==, DEFAULT_MTU);
+
+ success = g_attrib_set_mtu(cxt->att, 5);
+ g_assert(!success);
+
+ success = g_attrib_set_mtu(cxt->att, 255);
+ g_assert(success);
+
+ buf = g_attrib_get_buffer(cxt->att, &buflen);
+ g_assert(buf != 0);
+ g_assert_cmpint(buflen, ==, 255);
+}
+
+int main(int argc, char *argv[])
+{
+ g_test_init(&argc, &argv, NULL);
+
+ __btd_log_init("*", 0);
+
+ /*
+ * Test the GAttrib API behavior
+ */
+ g_test_add("/gattrib/refcount", struct context, NULL, setup_context,
+ test_refcount, teardown_context);
+ g_test_add("/gattrib/get_channel", struct context, NULL, setup_context,
+ test_get_channel, teardown_context);
+ g_test_add("/gattrib/send", struct context, NULL, setup_context,
+ test_send, teardown_context);
+ g_test_add("/gattrib/cancel", struct context, NULL, setup_context,
+ test_cancel, teardown_context);
+ g_test_add("/gattrib/register", struct context, NULL, setup_context,
+ test_register, teardown_context);
+ g_test_add("/gattrib/buffers", struct context, NULL, setup_context,
+ test_buffers, teardown_context);
+
+ return g_test_run();
+}
--
2.1.0.rc2.206.gedb03e5


2014-10-30 02:45:39

by Marie Janssen

[permalink] [raw]
Subject: [PATCH BlueZ v4 04/10] attrib: remove g_attrib_is_encrypted

This function is only used in one place and encryption is the
responsibility of the channel, not the attribute.
---
attrib/gattrib.c | 12 ------------
attrib/gattrib.h | 2 --
src/attrib-server.c | 12 ++++++++++++
3 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index a6511a2..a65d1ca 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -690,18 +690,6 @@ static int event_cmp_by_id(gconstpointer a, gconstpointer b)
return evt->id - id;
}

-gboolean g_attrib_is_encrypted(GAttrib *attrib)
-{
- BtIOSecLevel sec_level;
-
- if (!bt_io_get(attrib->io, NULL,
- BT_IO_OPT_SEC_LEVEL, &sec_level,
- BT_IO_OPT_INVALID))
- return FALSE;
-
- return sec_level > BT_IO_SEC_LOW;
-}
-
gboolean g_attrib_unregister(GAttrib *attrib, guint id)
{
struct event *evt;
diff --git a/attrib/gattrib.h b/attrib/gattrib.h
index 99b8b37..1557b99 100644
--- a/attrib/gattrib.h
+++ b/attrib/gattrib.h
@@ -62,8 +62,6 @@ guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
GAttribNotifyFunc func, gpointer user_data,
GDestroyNotify notify);

-gboolean g_attrib_is_encrypted(GAttrib *attrib);
-
uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len);
gboolean g_attrib_set_mtu(GAttrib *attrib, int mtu);

diff --git a/src/attrib-server.c b/src/attrib-server.c
index e65fff2..6571577 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -372,6 +372,18 @@ static struct attribute *attrib_db_add_new(struct gatt_server *server,
return a;
}

+static bool g_attrib_is_encrypted(GAttrib *attrib)
+{
+ BtIOSecLevel sec_level;
+ GIOChannel *io = g_attrib_get_channel(attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_SEC_LEVEL, &sec_level,
+ BT_IO_OPT_INVALID))
+ return FALSE;
+
+ return sec_level > BT_IO_SEC_LOW;
+}
+
static uint8_t att_check_reqs(struct gatt_channel *channel, uint8_t opcode,
int reqs)
{
--
2.1.0.rc2.206.gedb03e5