/*
 *
 *  Wireless daemon for Linux
 *
 *  Copyright (C) 2019  Intel Corporation. All rights reserved.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library 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
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; 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 <stdio.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <ell/ell.h>

#include "src/p2putil.h"
#include "src/ie.h"

static const uint8_t p2p_attrs1[] = {
	0x02, 0x02, 0x00, 0x25, 0x00, 0x0d, 0x1d, 0x00, 0x00, 0x28, 0xf8, 0xed,
	0x26, 0x57, 0x11, 0x08, 0x00, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x01,
	0x00, 0x10, 0x11, 0x00, 0x08, 0x74, 0x65, 0x73, 0x74, 0x64, 0x65, 0x76,
	0x31, 0x0f, 0x15, 0x00, 0x2a, 0xfe, 0xcd, 0x01, 0xbe, 0xa0, 0x44, 0x49,
	0x52, 0x45, 0x43, 0x54, 0x2d, 0x5a, 0x75, 0x2d, 0x41, 0x6e, 0x64, 0x72,
	0x65,
};

static void p2p_test_iter_sanity_check(const void *data)
{
	struct p2p_attr_iter iter;

	p2p_attr_iter_init(&iter, p2p_attrs1, sizeof(p2p_attrs1));

	assert(p2p_attr_iter_next(&iter));
	assert(p2p_attr_iter_get_type(&iter) == P2P_ATTR_P2P_CAPABILITY);
	assert(p2p_attr_iter_get_length(&iter) == 2);

	assert(p2p_attr_iter_next(&iter));
	assert(p2p_attr_iter_get_type(&iter) == P2P_ATTR_P2P_DEVICE_INFO);
	assert(p2p_attr_iter_get_length(&iter) == 29);

	assert(p2p_attr_iter_next(&iter));
	assert(p2p_attr_iter_get_type(&iter) == P2P_ATTR_P2P_GROUP_ID);
	assert(p2p_attr_iter_get_length(&iter) == 21);

	assert(!p2p_attr_iter_next(&iter));
}

typedef bool (*test_queue_cmp_func)(const void *data1, const void *data2);

static bool test_queue_cmp(struct l_queue *q1, struct l_queue *q2,
				test_queue_cmp_func func)
{
	const struct l_queue_entry *entry1 = l_queue_get_entries(q1);
	const struct l_queue_entry *entry2 = l_queue_get_entries(q2);

	while (entry1 && entry2) {
		if (!func(entry1->data, entry2->data))
			return false;

		entry1 = entry1->next;
		entry2 = entry2->next;
	}

	return !entry1 && !entry2;
}

static bool p2p_noa_desc_cmp(const void *data1, const void *data2)
{
	const struct p2p_notice_of_absence_desc *desc1 = data1;
	const struct p2p_notice_of_absence_desc *desc2 = data2;

	if (desc1->count_type != desc2->count_type)
		return false;
	if (desc1->duration != desc2->duration)
		return false;
	if (desc1->interval != desc2->interval)
		return false;
	if (desc1->start_time != desc2->start_time)
		return false;

	return true;
}

/*
 * The attributes in a P2P IE are not explicitly required to be ordered
 * in the same way they're listed in each frame's format specification in
 * Wi-Fi P2P Technical Specification v1.7 and in fact some consumer devices
 * switch some attributes' order in their probe responses.  This compares
 * the contents of two sets of P2P IEs ignoring attribute order and payload
 * segmentation into individual P2P IEs.
 * TODO: Might also want to validate the WSC IEs written by the p2putil.c
 * builder functions.
 */
static bool p2p_payload_cmd(const uint8_t *ies1, size_t ies1_len,
				const uint8_t *ies2, size_t ies2_len)
{
	uint8_t *payload1, *payload2;
	ssize_t payload1_len, payload2_len;
	struct p2p_attr_iter iter1, iter2;
	const uint8_t *attr1_start[P2P_ATTR_PERSISTENT_GROUP_INFO + 1];
	int i;
	bool r = false;

	if (!ies1 || !ies2)
		return false;

	payload1 = ie_tlv_extract_p2p_payload(ies1, ies1_len, &payload1_len);
	payload2 = ie_tlv_extract_p2p_payload(ies2, ies2_len, &payload2_len);

	if (payload1_len < 0 || payload2_len < 0)
		return false;

	p2p_attr_iter_init(&iter1, payload1, payload1_len);
	p2p_attr_iter_init(&iter2, payload2, payload2_len);

	memset(attr1_start, 0, sizeof(attr1_start));

	while (p2p_attr_iter_next(&iter1)) {
		enum p2p_attr type = p2p_attr_iter_get_type(&iter1);
		const uint8_t *start = p2p_attr_iter_get_data(&iter1) - 3;

		if (type >= L_ARRAY_SIZE(attr1_start))
			goto done;	/* Unknown attribute */

		if (attr1_start[type])
			goto done;	/* Duplicate attribute type in @ies1 */

		attr1_start[type] = start;
	}

	while (p2p_attr_iter_next(&iter2)) {
		enum p2p_attr type = p2p_attr_iter_get_type(&iter2);
		const uint8_t *start = p2p_attr_iter_get_data(&iter2) - 3;
		size_t len;

		if ((int) type >= (int) L_ARRAY_SIZE(attr1_start))
			goto done;	/* Unknown attribute */

		if (!attr1_start[type])
			goto done;	/* Not in @ies1 or dupe in @ies2 */

		len = p2p_attr_iter_get_length(&iter2) + 3;

		/*
		 * It's safe to memcmp len bytes because the length is also
		 * encoded in the first 3 bytes of both buffers.
		 */
		if (memcmp(start, attr1_start[type], len))
			goto done;	/* Contents or lengths differ */

		attr1_start[type] = NULL;
	}

	for (i = 0; i < (int) L_ARRAY_SIZE(attr1_start); i++)
		if (attr1_start[i])
			goto done;	/* @ies1 attribute was not in @ies2 */

	r = true;

done:
	l_free(payload1);
	l_free(payload2);
	return r;
}

static const uint8_t p2p_beacon_ies_1[] = {
	0xdd, 0x12, 0x50, 0x6f, 0x9a, 0x09, 0x02, 0x02, 0x00, 0x05, 0xab, 0x03,
	0x06, 0x00, 0x2a, 0xfe, 0xcd, 0x01, 0xbe, 0xa0, 0xdd, 0x4e, 0x00, 0x50,
	0xf2, 0x04, 0x10, 0x4a, 0x00, 0x01, 0x10, 0x10, 0x44, 0x00, 0x01, 0x02,
	0x10, 0x41, 0x00, 0x01, 0x01, 0x10, 0x12, 0x00, 0x02, 0x00, 0x04, 0x10,
	0x53, 0x00, 0x02, 0x43, 0x88, 0x10, 0x49, 0x00, 0x0e, 0x00, 0x37, 0x2a,
	0x00, 0x01, 0x20, 0x01, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x10,
	0x11, 0x00, 0x0d, 0x4c, 0x45, 0x58, 0x36, 0x32, 0x36, 0x2d, 0x50, 0x64,
	0x61, 0x4e, 0x65, 0x74, 0x10, 0x54, 0x00, 0x08, 0x00, 0x08, 0x00, 0x50,
	0xf2, 0x04, 0x00, 0x02, 0xdd, 0x16, 0x50, 0x6f, 0x9a, 0x09, 0x0c, 0x0f,
	0x00, 0xe1, 0x00, 0x01, 0xb0, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x7c, 0x45, 0x8f, 0x0c,
};

struct p2p_beacon_data {
	const uint8_t *ies;
	size_t ies_len;
	ssize_t payload_len;
	struct p2p_beacon data;
	const struct p2p_notice_of_absence_desc *noa_descs;
};

static const struct p2p_beacon_data p2p_beacon_data_1 = {
	.ies = p2p_beacon_ies_1,
	.ies_len = L_ARRAY_SIZE(p2p_beacon_ies_1),
	.payload_len = 32,
	.data = {
		.capability = {
			.device_caps = P2P_DEVICE_CAP_SVC_DISCOVERY |
				P2P_DEVICE_CAP_CONCURRENT_OP,
			.group_caps = P2P_GROUP_CAP_GO |
				P2P_GROUP_CAP_PERSISTENT_GROUP |
				P2P_GROUP_CAP_INTRA_BSS_DISTRIBUTION |
				P2P_GROUP_CAP_PERSISTENT_RECONNECT |
				P2P_GROUP_CAP_IP_ALLOCATION,
		},
		.device_addr = { 0x2a, 0xfe, 0xcd, 0x01, 0xbe, 0xa0 },
		.notice_of_absence = {
			.index = 225,
			.opp_ps = false,
			.ct_window = 0,
		},
	},
	.noa_descs = (const struct p2p_notice_of_absence_desc []) {
		[0] = {
			.count_type = 1,
			.duration = 46000,
			.interval = 0,
			.start_time = 210716028,
		},
		[1] = {}
	},
};

static void p2p_test_parse_beacon(const void *data)
{
	const struct p2p_beacon_data *test = data;
	struct p2p_beacon attrs1, attrs2;
	const struct p2p_notice_of_absence_desc *desc = test->noa_descs;
	uint8_t *payload;
	ssize_t payload_len;

	payload = ie_tlv_extract_p2p_payload(test->ies, test->ies_len,
						&payload_len);
	l_free(payload);
	assert(payload_len == test->payload_len);

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	if (desc)
		attrs2.notice_of_absence.descriptors = l_queue_new();

	while (desc && desc->start_time) {
		l_queue_push_tail(attrs2.notice_of_absence.descriptors,
					l_memdup(desc, sizeof(*desc)));
		desc++;
	}

	assert(p2p_parse_beacon(test->ies, test->ies_len, &attrs1) == 0);

	assert(attrs1.capability.device_caps == attrs2.capability.device_caps);
	assert(attrs1.capability.group_caps == attrs2.capability.group_caps);

	assert(!memcmp(attrs1.device_addr, attrs2.device_addr, 6));

	assert(attrs1.notice_of_absence.index ==
		attrs2.notice_of_absence.index);
	assert(attrs1.notice_of_absence.opp_ps ==
		attrs2.notice_of_absence.opp_ps);
	assert(attrs1.notice_of_absence.ct_window ==
		attrs2.notice_of_absence.ct_window);
	assert(test_queue_cmp(attrs1.notice_of_absence.descriptors,
				attrs2.notice_of_absence.descriptors,
				p2p_noa_desc_cmp));

	p2p_clear_beacon(&attrs1);
	p2p_clear_beacon(&attrs2);
}

static void p2p_test_build_beacon(const void *data)
{
	const struct p2p_beacon_data *test = data;
	struct p2p_beacon attrs2;
	const struct p2p_notice_of_absence_desc *desc = test->noa_descs;
	uint8_t *ies;
	size_t ies_len;

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	if (desc)
		attrs2.notice_of_absence.descriptors = l_queue_new();

	while (desc && desc->start_time) {
		l_queue_push_tail(attrs2.notice_of_absence.descriptors,
					l_memdup(desc, sizeof(*desc)));
		desc++;
	}

	ies = p2p_build_beacon(&attrs2, &ies_len);
	p2p_clear_beacon(&attrs2);

	assert(p2p_payload_cmd(ies, ies_len, test->ies, test->ies_len));
	l_free(ies);
}

static const uint8_t p2p_probe_req_ies_1[] = {
	0xdd, 0x73, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x4a, 0x00, 0x01, 0x10, 0x10,
	0x3a, 0x00, 0x01, 0x01, 0x10, 0x08, 0x00, 0x02, 0x31, 0x48, 0x10, 0x47,
	0x00, 0x10, 0x9a, 0xdd, 0x77, 0x82, 0xcb, 0xa6, 0x5e, 0x2d, 0xac, 0xd9,
	0xc0, 0x54, 0x34, 0xd0, 0xd7, 0x29, 0x10, 0x54, 0x00, 0x08, 0x00, 0x01,
	0x00, 0x50, 0xf2, 0x04, 0x00, 0x01, 0x10, 0x3c, 0x00, 0x01, 0x03, 0x10,
	0x02, 0x00, 0x02, 0x00, 0x00, 0x10, 0x09, 0x00, 0x02, 0x00, 0x00, 0x10,
	0x12, 0x00, 0x02, 0x00, 0x00, 0x10, 0x21, 0x00, 0x01, 0x20, 0x10, 0x23,
	0x00, 0x01, 0x20, 0x10, 0x24, 0x00, 0x01, 0x20, 0x10, 0x11, 0x00, 0x08,
	0x74, 0x65, 0x73, 0x74, 0x64, 0x65, 0x76, 0x31, 0x10, 0x49, 0x00, 0x09,
	0x00, 0x37, 0x2a, 0x00, 0x01, 0x20, 0x03, 0x01, 0x01, 0xdd, 0x11, 0x50,
	0x6f, 0x9a, 0x09, 0x02, 0x02, 0x00, 0x25, 0x00, 0x06, 0x05, 0x00, 0x58,
	0x58, 0x04, 0x51, 0x01,
};

struct p2p_probe_req_data {
	const uint8_t *ies;
	size_t ies_len;
	ssize_t payload_len;
	struct p2p_probe_req data;
};

static const struct p2p_probe_req_data p2p_probe_req_data_1 = {
	.ies = p2p_probe_req_ies_1,
	.ies_len = L_ARRAY_SIZE(p2p_probe_req_ies_1),
	.payload_len = 13,
	.data = {
		.capability = {
			.device_caps = P2P_DEVICE_CAP_SVC_DISCOVERY |
				P2P_DEVICE_CAP_CONCURRENT_OP |
				P2P_DEVICE_CAP_INVITATION_PROCEDURE,
			.group_caps = 0,
		},
		.listen_channel = {
			.country = { 'X', 'X', '\x04' },
			.oper_class = 81,
			.channel_num = 1,
		},
	},
};

static void p2p_test_parse_probe_req(const void *data)
{
	const struct p2p_probe_req_data *test = data;
	uint8_t *payload;
	ssize_t payload_len;
	struct p2p_probe_req attrs;

	payload = ie_tlv_extract_p2p_payload(test->ies, test->ies_len,
						&payload_len);
	l_free(payload);
	assert(payload_len == test->payload_len);

	assert(p2p_parse_probe_req(test->ies, test->ies_len, &attrs) == 0);

	assert(attrs.capability.device_caps ==
		test->data.capability.device_caps);
	assert(attrs.capability.group_caps == test->data.capability.group_caps);

	assert(!memcmp(attrs.device_addr, test->data.device_addr, 6));

	assert(!memcmp(attrs.listen_channel.country,
			test->data.listen_channel.country, 3));
	assert(attrs.listen_channel.oper_class ==
		test->data.listen_channel.oper_class);
	assert(attrs.listen_channel.channel_num ==
		test->data.listen_channel.channel_num);

	assert(attrs.listen_availability.avail_period_ms ==
		test->data.listen_availability.avail_period_ms);
	assert(attrs.listen_availability.avail_interval_ms ==
		test->data.listen_availability.avail_interval_ms);

	assert(!memcmp(attrs.device_info.device_addr,
			test->data.device_info.device_addr, 6));
	assert(attrs.device_info.wsc_config_methods ==
		test->data.device_info.wsc_config_methods);
	assert(attrs.device_info.primary_device_type.category ==
		test->data.device_info.primary_device_type.category);
	assert(!memcmp(attrs.device_info.primary_device_type.oui,
			test->data.device_info.primary_device_type.oui, 3));
	assert(attrs.device_info.primary_device_type.oui_type ==
		test->data.device_info.primary_device_type.oui_type);
	assert(attrs.device_info.primary_device_type.subcategory ==
		test->data.device_info.primary_device_type.subcategory);
	assert(l_queue_length(attrs.device_info.secondary_device_types) ==
		l_queue_length(test->data.device_info.secondary_device_types));
	assert(!strcmp(attrs.device_info.device_name,
			test->data.device_info.device_name));

	assert(!memcmp(attrs.operating_channel.country,
			test->data.operating_channel.country, 3));
	assert(attrs.operating_channel.oper_class ==
		test->data.operating_channel.oper_class);
	assert(attrs.operating_channel.channel_num ==
		test->data.operating_channel.channel_num);

	assert(l_queue_length(attrs.service_hashes) ==
		l_queue_length(test->data.service_hashes));

	p2p_clear_probe_req(&attrs);
}

static void p2p_test_build_probe_req(const void *data)
{
	const struct p2p_probe_req_data *test = data;
	uint8_t *ies;
	size_t ies_len;

	ies = p2p_build_probe_req(&test->data, &ies_len);

	assert(p2p_payload_cmd(ies, ies_len, test->ies, test->ies_len));
	l_free(ies);
}

/* Notice of Absence and Device Info in the wrong order */
static const uint8_t p2p_probe_resp_ies_1[] = {
	0xdd, 0xa2, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x4a, 0x00, 0x01, 0x10, 0x10,
	0x44, 0x00, 0x01, 0x02, 0x10, 0x41, 0x00, 0x01, 0x01, 0x10, 0x12, 0x00,
	0x02, 0x00, 0x04, 0x10, 0x53, 0x00, 0x02, 0x43, 0x88, 0x10, 0x3b, 0x00,
	0x01, 0x03, 0x10, 0x47, 0x00, 0x10, 0xb1, 0x7d, 0x6f, 0xc9, 0x4f, 0xd1,
	0x5a, 0x6f, 0xb6, 0x50, 0x53, 0x11, 0x0b, 0x2a, 0xb5, 0x25, 0x10, 0x21,
	0x00, 0x0d, 0x4d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x65, 0x6b, 0x20, 0x49,
	0x6e, 0x63, 0x2e, 0x10, 0x23, 0x00, 0x12, 0x4d, 0x54, 0x4b, 0x20, 0x57,
	0x69, 0x72, 0x65, 0x6c, 0x65, 0x73, 0x73, 0x20, 0x4d, 0x6f, 0x64, 0x65,
	0x6c, 0x10, 0x24, 0x00, 0x03, 0x31, 0x2e, 0x30, 0x10, 0x42, 0x00, 0x03,
	0x32, 0x2e, 0x30, 0x10, 0x54, 0x00, 0x08, 0x00, 0x08, 0x00, 0x50, 0xf2,
	0x04, 0x00, 0x02, 0x10, 0x11, 0x00, 0x0d, 0x4c, 0x45, 0x58, 0x36, 0x32,
	0x36, 0x2d, 0x50, 0x64, 0x61, 0x4e, 0x65, 0x74, 0x10, 0x08, 0x00, 0x02,
	0x41, 0x08, 0x10, 0x49, 0x00, 0x0e, 0x00, 0x37, 0x2a, 0x00, 0x01, 0x20,
	0x01, 0x06, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xdd, 0x27, 0x50, 0x6f,
	0x9a, 0x09, 0x02, 0x02, 0x00, 0x05, 0xab, 0x0d, 0x1b, 0x00, 0x2a, 0xfe,
	0xcd, 0x01, 0xbe, 0xa0, 0x01, 0x88, 0x00, 0x08, 0x00, 0x50, 0xf2, 0x04,
	0x00, 0x02, 0x00, 0x10, 0x11, 0x00, 0x06, 0x4d, 0x6f, 0x62, 0x69, 0x6c,
	0x65, 0xdd, 0x16, 0x50, 0x6f, 0x9a, 0x09, 0x0c, 0x0f, 0x00, 0xe1, 0x00,
	0x01, 0xb0, 0xb3, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7c, 0x45, 0x8f,
	0x0c,
};

struct p2p_probe_resp_data {
	const uint8_t *ies;
	size_t ies_len;
	ssize_t payload_len;
	struct p2p_probe_resp data;
	const struct p2p_notice_of_absence_desc *noa_descs;
};

static const struct p2p_probe_resp_data p2p_probe_resp_data_1 = {
	.ies = p2p_probe_resp_ies_1,
	.ies_len = L_ARRAY_SIZE(p2p_probe_resp_ies_1),
	.payload_len = 53,
	.data = {
		.capability = {
			.device_caps = P2P_DEVICE_CAP_SVC_DISCOVERY |
				P2P_DEVICE_CAP_CONCURRENT_OP,
			.group_caps = P2P_GROUP_CAP_GO |
				P2P_GROUP_CAP_PERSISTENT_GROUP |
				P2P_GROUP_CAP_INTRA_BSS_DISTRIBUTION |
				P2P_GROUP_CAP_PERSISTENT_RECONNECT |
				P2P_GROUP_CAP_IP_ALLOCATION,
		},
		.notice_of_absence = {
			.index = 225,
			.opp_ps = false,
			.ct_window = 0,
		},
		.device_info = {
			.device_addr = { 0x2a, 0xfe, 0xcd, 0x01, 0xbe, 0xa0 },
			.wsc_config_methods = WSC_CONFIGURATION_METHOD_DISPLAY |
				WSC_CONFIGURATION_METHOD_PUSH_BUTTON |
				WSC_CONFIGURATION_METHOD_KEYPAD,
			.primary_device_type = {
				.category = 8,
				.oui = { 0x00, 0x50, 0xf2 },
				.oui_type = 0x04,
				.subcategory = 2,
			},
			.device_name = "Mobile",
		},
	},
	.noa_descs = (const struct p2p_notice_of_absence_desc []) {
		[0] = {
			.count_type = 1,
			.duration = 46000,
			.interval = 0,
			.start_time = 210716028,
		},
		[1] = {}
	},
};

static const uint8_t p2p_probe_resp_ies_2[] = {
	0xdd, 0x3e, 0x50, 0x6f, 0x9a, 0x09, 0x02, 0x02, 0x00, 0x05, 0x01, 0x0d,
	0x32, 0x00, 0xa2, 0x8c, 0xfd, 0xb9, 0x05, 0xef, 0x5a, 0x88, 0x00, 0x03,
	0x00, 0x50, 0xf2, 0x04, 0x00, 0x01, 0x00, 0x10, 0x11, 0x00, 0x1d, 0x44,
	0x49, 0x52, 0x45, 0x43, 0x54, 0x2d, 0x45, 0x46, 0x2d, 0x48, 0x50, 0x20,
	0x45, 0x4e, 0x56, 0x59, 0x20, 0x34, 0x35, 0x32, 0x30, 0x20, 0x73, 0x65,
	0x72, 0x69, 0x65, 0x73, 0xdd, 0xc1, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x4a,
	0x00, 0x01, 0x10, 0x10, 0x44, 0x00, 0x01, 0x02, 0x10, 0x57, 0x00, 0x01,
	0x01, 0x10, 0x41, 0x00, 0x01, 0x00, 0x10, 0x3b, 0x00, 0x01, 0x03, 0x10,
	0x47, 0x00, 0x10, 0x1c, 0x85, 0x2a, 0x4d, 0xb8, 0x00, 0x1f, 0x08, 0xab,
	0xcd, 0xa0, 0x8c, 0xfd, 0xb9, 0x05, 0xef, 0x10, 0x21, 0x00, 0x02, 0x48,
	0x50, 0x10, 0x23, 0x00, 0x11, 0x45, 0x4e, 0x56, 0x59, 0x20, 0x34, 0x35,
	0x32, 0x30, 0x20, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73, 0x00, 0x10, 0x24,
	0x00, 0x05, 0x34, 0x35, 0x32, 0x37, 0x00, 0x10, 0x42, 0x00, 0x10, 0x54,
	0x48, 0x36, 0x35, 0x4f, 0x33, 0x48, 0x31, 0x58, 0x59, 0x30, 0x36, 0x36,
	0x30, 0x00, 0x00, 0x10, 0x54, 0x00, 0x08, 0x00, 0x03, 0x00, 0x50, 0xf2,
	0x04, 0x00, 0x05, 0x10, 0x11, 0x00, 0x1d, 0x44, 0x49, 0x52, 0x45, 0x43,
	0x54, 0x2d, 0x45, 0x46, 0x2d, 0x48, 0x50, 0x20, 0x45, 0x4e, 0x56, 0x59,
	0x20, 0x34, 0x35, 0x32, 0x30, 0x20, 0x73, 0x65, 0x72, 0x69, 0x65, 0x73,
	0x10, 0x08, 0x00, 0x02, 0x00, 0x00, 0x10, 0x49, 0x00, 0x06, 0x00, 0x37,
	0x2a, 0x00, 0x01, 0x20, 0x10, 0x49, 0x00, 0x17, 0x00, 0x01, 0x37, 0x10,
	0x06, 0x00, 0x10, 0x1c, 0x85, 0x2a, 0x4d, 0xb8, 0x00, 0x1f, 0x08, 0xab,
	0xcd, 0xa0, 0x8c, 0xfd, 0xb9, 0x05, 0xef,
};

static const struct p2p_probe_resp_data p2p_probe_resp_data_2 = {
	.ies = p2p_probe_resp_ies_2,
	.ies_len = L_ARRAY_SIZE(p2p_probe_resp_ies_2),
	.payload_len = 58,
	.data = {
		.capability = {
			.device_caps = P2P_DEVICE_CAP_SVC_DISCOVERY |
				P2P_DEVICE_CAP_CONCURRENT_OP,
			.group_caps = P2P_GROUP_CAP_GO,
		},
		.device_info = {
			.device_addr = { 0xa2, 0x8c, 0xfd, 0xb9, 0x05, 0xef },
			.wsc_config_methods =
				WSC_CONFIGURATION_METHOD_VIRTUAL_PUSH_BUTTON |
				0x0800 | WSC_CONFIGURATION_METHOD_P2P |
				WSC_CONFIGURATION_METHOD_PHYSICAL_DISPLAY_PIN,
			.primary_device_type = {
				.category = 3,
				.oui = { 0x00, 0x50, 0xf2 },
				.oui_type = 0x04,
				.subcategory = 1,
			},
			.device_name = "DIRECT-EF-HP ENVY 4520 series",
		},
	},
};

static void p2p_test_parse_probe_resp(const void *data)
{
	const struct p2p_probe_resp_data *test = data;
	struct p2p_probe_resp attrs1, attrs2;
	const struct p2p_notice_of_absence_desc *desc = test->noa_descs;
	uint8_t *payload;
	ssize_t payload_len;

	payload = ie_tlv_extract_p2p_payload(test->ies, test->ies_len,
						&payload_len);
	l_free(payload);
	assert(payload_len == test->payload_len);

	assert(p2p_parse_probe_resp(test->ies, test->ies_len, &attrs1) == 0);

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	if (desc)
		attrs2.notice_of_absence.descriptors = l_queue_new();

	while (desc && desc->start_time) {
		l_queue_push_tail(attrs2.notice_of_absence.descriptors,
					l_memdup(desc, sizeof(*desc)));
		desc++;
	}

	assert(attrs1.capability.device_caps == attrs2.capability.device_caps);
	assert(attrs1.capability.group_caps == attrs2.capability.group_caps);

	assert(attrs1.listen_availability.avail_period_ms ==
		attrs2.listen_availability.avail_period_ms);
	assert(attrs1.listen_availability.avail_interval_ms ==
		attrs2.listen_availability.avail_interval_ms);

	assert(attrs1.notice_of_absence.index ==
		attrs2.notice_of_absence.index);
	assert(attrs1.notice_of_absence.opp_ps ==
		attrs2.notice_of_absence.opp_ps);
	assert(attrs1.notice_of_absence.ct_window ==
		attrs2.notice_of_absence.ct_window);
	assert(test_queue_cmp(attrs1.notice_of_absence.descriptors,
				attrs2.notice_of_absence.descriptors,
				p2p_noa_desc_cmp));

	assert(!memcmp(attrs1.device_info.device_addr,
			attrs2.device_info.device_addr, 6));
	assert(attrs1.device_info.wsc_config_methods ==
		attrs2.device_info.wsc_config_methods);
	assert(attrs1.device_info.primary_device_type.category ==
		attrs2.device_info.primary_device_type.category);
	assert(!memcmp(attrs1.device_info.primary_device_type.oui,
			attrs2.device_info.primary_device_type.oui, 3));
	assert(attrs1.device_info.primary_device_type.oui_type ==
		attrs2.device_info.primary_device_type.oui_type);
	assert(attrs1.device_info.primary_device_type.subcategory ==
		attrs2.device_info.primary_device_type.subcategory);
	assert(l_queue_length(attrs1.device_info.secondary_device_types) ==
		l_queue_length(attrs2.device_info.secondary_device_types));
	assert(!strcmp(attrs1.device_info.device_name,
			attrs2.device_info.device_name));

	assert(l_queue_length(attrs1.group_clients) ==
		l_queue_length(attrs2.group_clients));

	assert(l_queue_length(attrs1.advertised_svcs) ==
		l_queue_length(attrs2.advertised_svcs));

	p2p_clear_probe_resp(&attrs1);
	p2p_clear_probe_resp(&attrs2);
}

static void p2p_test_build_probe_resp(const void *data)
{
	const struct p2p_probe_resp_data *test = data;
	struct p2p_probe_resp attrs2;
	const struct p2p_notice_of_absence_desc *desc = test->noa_descs;
	uint8_t *ies;
	size_t ies_len;

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	if (desc)
		attrs2.notice_of_absence.descriptors = l_queue_new();

	while (desc && desc->start_time) {
		l_queue_push_tail(attrs2.notice_of_absence.descriptors,
					l_memdup(desc, sizeof(*desc)));
		desc++;
	}

	ies = p2p_build_probe_resp(&attrs2, &ies_len);
	p2p_clear_probe_resp(&attrs2);

	assert(p2p_payload_cmd(ies, ies_len, test->ies, test->ies_len));
	l_free(ies);
}

static const uint8_t p2p_association_req_ies_1[] = {
	0xdd, 0x18, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x4a, 0x00, 0x01, 0x10, 0x10,
	0x3a, 0x00, 0x01, 0x01, 0x10, 0x49, 0x00, 0x06, 0x00, 0x37, 0x2a, 0x00,
	0x01, 0x20, 0xdd, 0x29, 0x50, 0x6f, 0x9a, 0x09, 0x02, 0x02, 0x00, 0x27,
	0x00, 0x0d, 0x1d, 0x00, 0x00, 0x28, 0xf8, 0xed, 0x26, 0x57, 0x11, 0x08,
	0x00, 0x01, 0x00, 0x50, 0xf2, 0x04, 0x00, 0x01, 0x00, 0x10, 0x11, 0x00,
	0x08, 0x74, 0x65, 0x73, 0x74, 0x64, 0x65, 0x76, 0x31,
};

struct p2p_association_req_data {
	const uint8_t *ies;
	size_t ies_len;
	ssize_t payload_len;
	struct p2p_association_req data;
};

static const struct p2p_association_req_data p2p_association_req_data_1 = {
	.ies = p2p_association_req_ies_1,
	.ies_len = L_ARRAY_SIZE(p2p_association_req_ies_1),
	.payload_len = 37,
	.data = {
		.capability = {
			.device_caps = P2P_DEVICE_CAP_SVC_DISCOVERY |
				P2P_DEVICE_CAP_CLIENT_DISCOVERABILITY |
				P2P_DEVICE_CAP_CONCURRENT_OP |
				P2P_DEVICE_CAP_INVITATION_PROCEDURE,
			.group_caps = 0,
		},
		.device_info = {
			.device_addr = { 0x00, 0x28, 0xf8, 0xed, 0x26, 0x57 },
			.wsc_config_methods = WSC_CONFIGURATION_METHOD_DISPLAY |
				WSC_CONFIGURATION_METHOD_KEYPAD |
				WSC_CONFIGURATION_METHOD_P2P,
			.primary_device_type = {
				.category = 1,
				.oui = { 0x00, 0x50, 0xf2 },
				.oui_type = 0x04,
				.subcategory = 1,
			},
			.device_name = "testdev1",
		},
	},
};

static void p2p_test_parse_association_req(const void *data)
{
	const struct p2p_association_req_data *test = data;
	struct p2p_association_req attrs1, attrs2;
	uint8_t *payload;
	ssize_t payload_len;

	payload = ie_tlv_extract_p2p_payload(test->ies, test->ies_len,
						&payload_len);
	l_free(payload);
	assert(payload_len == test->payload_len);

	assert(p2p_parse_association_req(test->ies, test->ies_len, &attrs1) ==
		0);

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	assert(attrs1.capability.device_caps == attrs2.capability.device_caps);
	assert(attrs1.capability.group_caps == attrs2.capability.group_caps);

	assert(attrs1.listen_availability.avail_period_ms ==
		attrs2.listen_availability.avail_period_ms);
	assert(attrs1.listen_availability.avail_interval_ms ==
		attrs2.listen_availability.avail_interval_ms);

	assert(!memcmp(attrs1.device_info.device_addr,
			attrs2.device_info.device_addr, 6));
	assert(attrs1.device_info.wsc_config_methods ==
		attrs2.device_info.wsc_config_methods);
	assert(attrs1.device_info.primary_device_type.category ==
		attrs2.device_info.primary_device_type.category);
	assert(!memcmp(attrs1.device_info.primary_device_type.oui,
			attrs2.device_info.primary_device_type.oui, 3));
	assert(attrs1.device_info.primary_device_type.oui_type ==
		attrs2.device_info.primary_device_type.oui_type);
	assert(attrs1.device_info.primary_device_type.subcategory ==
		attrs2.device_info.primary_device_type.subcategory);
	assert(l_queue_length(attrs1.device_info.secondary_device_types) ==
		l_queue_length(attrs2.device_info.secondary_device_types));
	assert(!strcmp(attrs1.device_info.device_name,
			attrs2.device_info.device_name));

	assert(!memcmp(attrs1.interface.device_addr,
			attrs2.interface.device_addr, 6));
	assert(l_queue_length(attrs1.interface.interface_addrs) ==
		l_queue_length(attrs2.interface.interface_addrs));

	p2p_clear_association_req(&attrs1);
	p2p_clear_association_req(&attrs2);
}

static void p2p_test_build_association_req(const void *data)
{
	const struct p2p_association_req_data *test = data;
	uint8_t *ies;
	size_t ies_len;

	ies = p2p_build_association_req(&test->data, &ies_len);

	assert(p2p_payload_cmd(ies, ies_len, test->ies, test->ies_len));
	l_free(ies);
}

/* Obligatory P2P IE empty here */
static const uint8_t p2p_association_resp_ies_1[] = {
	0x01, 0x08, 0x8c, 0x12, 0x98, 0x24, 0xb0, 0x48, 0x60, 0x6c, 0x2d, 0x1a,
	0x73, 0x11, 0x03, 0xff, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x3d, 0x16, 0x64, 0x05, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0xdd, 0x04, 0x50, 0x6f, 0x9a, 0x09, 0x7f, 0x08, 0x01, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0xbf, 0x0c, 0x30, 0x71, 0x90, 0x03,
	0xfe, 0xff, 0x00, 0x00, 0xfe, 0xff, 0x00, 0x00, 0xc0, 0x05, 0x00, 0x00,
	0x00, 0xfe, 0xff, 0xdd, 0x18, 0x00, 0x50, 0xf2, 0x02, 0x01, 0x01, 0x80,
	0x01, 0x03, 0xa4, 0x00, 0x00, 0x27, 0xa4, 0x00, 0x00, 0x42, 0x43, 0x5e,
	0x00, 0x62, 0x32, 0x2f, 0x00, 0xdd, 0x07, 0x00, 0x0c, 0xe7, 0x08, 0x00,
	0x00, 0x00,
};

struct p2p_association_resp_data {
	const uint8_t *ies;
	size_t ies_len;
	ssize_t payload_len;
	struct p2p_association_resp data;
};

static const struct p2p_association_resp_data p2p_association_resp_data_1 = {
	.ies = p2p_association_resp_ies_1,
	.ies_len = L_ARRAY_SIZE(p2p_association_resp_ies_1),
	.payload_len = 0,
	.data = {},
};

static void p2p_test_parse_association_resp(const void *data)
{
	const struct p2p_association_resp_data *test = data;
	struct p2p_association_resp attrs1, attrs2;
	uint8_t *payload;
	ssize_t payload_len;

	payload = ie_tlv_extract_p2p_payload(test->ies, test->ies_len,
						&payload_len);
	l_free(payload);
	assert(payload_len == test->payload_len);

	assert(p2p_parse_association_resp(test->ies, test->ies_len, &attrs1) ==
		0);

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	assert(attrs1.status == attrs2.status);

	assert(attrs1.listen_availability.avail_period_ms ==
		attrs2.listen_availability.avail_period_ms);
	assert(attrs1.listen_availability.avail_interval_ms ==
		attrs2.listen_availability.avail_interval_ms);

	p2p_clear_association_resp(&attrs1);
	p2p_clear_association_resp(&attrs2);
}

static void p2p_test_build_association_resp(const void *data)
{
	const struct p2p_association_resp_data *test = data;
	uint8_t *ies;
	size_t ies_len;

	ies = p2p_build_association_resp(&test->data, &ies_len);

	assert(p2p_payload_cmd(ies, ies_len, test->ies, test->ies_len));
	l_free(ies);
}

static const uint8_t p2p_provision_disc_req_1[] = {
	0x04, 0x09, 0x50, 0x6f, 0x9a, 0x09, 0x07, 0x01, 0xdd, 0x41, 0x50, 0x6f,
	0x9a, 0x09, 0x02, 0x02, 0x00, 0x25, 0x00, 0x0d, 0x1d, 0x00, 0x00, 0x28,
	0xf8, 0xed, 0x26, 0x57, 0x11, 0x08, 0x00, 0x01, 0x00, 0x50, 0xf2, 0x04,
	0x00, 0x01, 0x00, 0x10, 0x11, 0x00, 0x08, 0x74, 0x65, 0x73, 0x74, 0x64,
	0x65, 0x76, 0x31, 0x0f, 0x15, 0x00, 0x2a, 0xfe, 0xcd, 0x01, 0xbe, 0xa0,
	0x44, 0x49, 0x52, 0x45, 0x43, 0x54, 0x2d, 0x5a, 0x75, 0x2d, 0x54, 0x65,
	0x73, 0x74, 0x31, 0xdd, 0x0a, 0x00, 0x50, 0xf2, 0x04, 0x10, 0x08, 0x00,
	0x02, 0x00, 0x80,
};

struct p2p_provision_disc_req_data {
	const uint8_t *frame;
	size_t frame_len;
	ssize_t payload_len;
	struct p2p_provision_discovery_req data;
};

static const struct p2p_provision_disc_req_data p2p_provision_disc_req_data_1 =
{
	.frame = p2p_provision_disc_req_1,
	.frame_len = L_ARRAY_SIZE(p2p_provision_disc_req_1),
	.payload_len = 61,
	.data = {
		.dialog_token = 1,
		.capability = {
			.device_caps = P2P_DEVICE_CAP_SVC_DISCOVERY |
				P2P_DEVICE_CAP_CONCURRENT_OP |
				P2P_DEVICE_CAP_INVITATION_PROCEDURE,
			.group_caps = 0,
		},
		.device_info = {
			.device_addr = { 0x00, 0x28, 0xf8, 0xed, 0x26, 0x57 },
			.wsc_config_methods = WSC_CONFIGURATION_METHOD_DISPLAY |
				WSC_CONFIGURATION_METHOD_KEYPAD |
				WSC_CONFIGURATION_METHOD_P2P,
			.primary_device_type = {
				.category = 1,
				.oui = { 0x00, 0x50, 0xf2 },
				.oui_type = 0x04,
				.subcategory = 1,
			},
			.device_name = "testdev1",
		},
		.group_id = {
			.device_addr = { 0x2a, 0xfe, 0xcd, 0x01, 0xbe, 0xa0 },
			.ssid = "DIRECT-Zu-Test1",
		},
		.status = -1,
		.wsc_config_method = WSC_CONFIGURATION_METHOD_PUSH_BUTTON,
	},
};

static void p2p_test_parse_provision_disc_req(const void *data)
{
	const struct p2p_provision_disc_req_data *test = data;
	struct p2p_provision_discovery_req attrs1, attrs2;
	uint8_t *payload;
	ssize_t payload_len;

	payload = ie_tlv_extract_p2p_payload(test->frame + 8,
						test->frame_len - 8,
						&payload_len);
	l_free(payload);
	assert(payload_len == test->payload_len);

	assert(p2p_parse_provision_disc_req(test->frame + 7,
						test->frame_len - 7,
						&attrs1) == 0);

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	assert(attrs1.dialog_token == attrs2.dialog_token);

	assert(attrs1.capability.device_caps == attrs2.capability.device_caps);
	assert(attrs1.capability.group_caps == attrs2.capability.group_caps);

	assert(!memcmp(attrs1.device_info.device_addr,
			attrs2.device_info.device_addr, 6));
	assert(attrs1.device_info.wsc_config_methods ==
		attrs2.device_info.wsc_config_methods);
	assert(attrs1.device_info.primary_device_type.category ==
		attrs2.device_info.primary_device_type.category);
	assert(!memcmp(attrs1.device_info.primary_device_type.oui,
			attrs2.device_info.primary_device_type.oui, 3));
	assert(attrs1.device_info.primary_device_type.oui_type ==
		attrs2.device_info.primary_device_type.oui_type);
	assert(attrs1.device_info.primary_device_type.subcategory ==
		attrs2.device_info.primary_device_type.subcategory);
	assert(l_queue_length(attrs1.device_info.secondary_device_types) ==
		l_queue_length(attrs2.device_info.secondary_device_types));
	assert(!strcmp(attrs1.device_info.device_name,
			attrs2.device_info.device_name));

	assert(!memcmp(attrs1.group_id.device_addr,
			attrs2.group_id.device_addr, 6));
	assert(!strcmp(attrs1.group_id.ssid, attrs2.group_id.ssid));

	assert(!memcmp(attrs1.intended_interface_addr,
			attrs2.intended_interface_addr, 6));

	assert(attrs1.status == attrs2.status);

	assert(!memcmp(attrs1.operating_channel.country,
			attrs2.operating_channel.country, 3));
	assert(attrs1.operating_channel.oper_class ==
		attrs2.operating_channel.oper_class);
	assert(attrs1.operating_channel.channel_num ==
		attrs2.operating_channel.channel_num);

	assert(!memcmp(attrs1.channel_list.country,
			attrs2.channel_list.country, 3));
	assert(l_queue_length(attrs1.channel_list.channel_entries) ==
		l_queue_length(attrs2.channel_list.channel_entries));

	assert(attrs1.session_info.data_len == attrs2.session_info.data_len);
	assert(!attrs1.session_info.data_len ||
		!memcmp(attrs1.session_info.data, attrs2.session_info.data,
			attrs1.session_info.data_len));

	assert(attrs1.connection_capability == attrs2.connection_capability);

	assert(attrs1.advertisement_id.advertisement_id ==
		attrs2.advertisement_id.advertisement_id);
	assert(!memcmp(attrs1.advertisement_id.service_mac_addr,
			attrs2.advertisement_id.service_mac_addr, 6));

	assert(attrs1.config_timeout.go_config_timeout ==
		attrs2.config_timeout.go_config_timeout);
	assert(attrs1.config_timeout.client_config_timeout ==
		attrs2.config_timeout.client_config_timeout);

	assert(!memcmp(attrs1.listen_channel.country,
			attrs2.listen_channel.country, 3));
	assert(attrs1.listen_channel.oper_class ==
		attrs2.listen_channel.oper_class);
	assert(attrs1.listen_channel.channel_num ==
		attrs2.listen_channel.channel_num);

	assert(attrs1.session_id.session_id == attrs2.session_id.session_id);
	assert(!memcmp(attrs1.session_id.session_mac_addr,
			attrs2.session_id.session_mac_addr, 6));

	assert(attrs1.transport_protocol == attrs2.transport_protocol);

	assert(!memcmp(attrs1.persistent_group_info.device_addr,
			attrs2.persistent_group_info.device_addr, 6));
	assert(!strcmp(attrs1.persistent_group_info.ssid,
			attrs2.persistent_group_info.ssid));

	assert(attrs1.wsc_config_method == attrs2.wsc_config_method);

	p2p_clear_provision_disc_req(&attrs1);
	p2p_clear_provision_disc_req(&attrs2);
}

static void p2p_test_build_provision_disc_req(const void *data)
{
	const struct p2p_provision_disc_req_data *test = data;
	uint8_t *frame;
	size_t frame_len;

	frame = p2p_build_provision_disc_req(&test->data, &frame_len);

	assert(!memcmp(frame, test->frame, 8));
	assert(p2p_payload_cmd(frame + 8, frame_len - 8,
				test->frame + 8, test->frame_len - 8));
	l_free(frame);
}

/* The optional P2P IE not present here */
static const uint8_t p2p_provision_disc_resp_1[] = {
	0x04, 0x09, 0x50, 0x6f, 0x9a, 0x09, 0x08, 0x01, 0xdd, 0x0a, 0x00, 0x50,
	0xf2, 0x04, 0x10, 0x08, 0x00, 0x02, 0x00, 0x80, 0xdd, 0x0d, 0x50, 0x6f,
	0x9a, 0x0a, 0x00, 0x00, 0x06, 0x00, 0x11, 0x00, 0x00, 0x00, 0x32,
};

struct p2p_provision_disc_resp_data {
	const uint8_t *frame;
	size_t frame_len;
	ssize_t payload_len;
	struct p2p_provision_discovery_resp data;
};

static const struct p2p_provision_disc_resp_data
		p2p_provision_disc_resp_data_1 = {
	.frame = p2p_provision_disc_resp_1,
	.frame_len = L_ARRAY_SIZE(p2p_provision_disc_resp_1),
	.payload_len = -ENOENT,
	.data = {
		.dialog_token = 1,
		.status = -1,
		.wsc_config_method = WSC_CONFIGURATION_METHOD_PUSH_BUTTON,
	},
};

static void p2p_test_parse_provision_disc_resp(const void *data)
{
	const struct p2p_provision_disc_resp_data *test = data;
	struct p2p_provision_discovery_resp attrs1, attrs2;
	uint8_t *payload;
	ssize_t payload_len;

	payload = ie_tlv_extract_p2p_payload(test->frame + 8,
						test->frame_len - 8,
						&payload_len);
	if (payload_len >= 0)
		l_free(payload);

	assert(payload_len == test->payload_len);

	assert(p2p_parse_provision_disc_resp(test->frame + 7,
						test->frame_len - 7,
						&attrs1) == 0);

	memcpy(&attrs2, &test->data, sizeof(attrs2));

	assert(attrs1.dialog_token == attrs2.dialog_token);

	assert(attrs1.status == attrs2.status);

	assert(attrs1.capability.device_caps == attrs2.capability.device_caps);
	assert(attrs1.capability.group_caps == attrs2.capability.group_caps);

	assert(!memcmp(attrs1.device_info.device_addr,
			attrs2.device_info.device_addr, 6));
	assert(attrs1.device_info.wsc_config_methods ==
		attrs2.device_info.wsc_config_methods);
	assert(attrs1.device_info.primary_device_type.category ==
		attrs2.device_info.primary_device_type.category);
	assert(!memcmp(attrs1.device_info.primary_device_type.oui,
			attrs2.device_info.primary_device_type.oui, 3));
	assert(attrs1.device_info.primary_device_type.oui_type ==
		attrs2.device_info.primary_device_type.oui_type);
	assert(attrs1.device_info.primary_device_type.subcategory ==
		attrs2.device_info.primary_device_type.subcategory);
	assert(l_queue_length(attrs1.device_info.secondary_device_types) ==
		l_queue_length(attrs2.device_info.secondary_device_types));
	assert(!strcmp(attrs1.device_info.device_name,
			attrs2.device_info.device_name));

	assert(!memcmp(attrs1.group_id.device_addr,
			attrs2.group_id.device_addr, 6));
	assert(!strcmp(attrs1.group_id.ssid, attrs2.group_id.ssid));

	assert(!memcmp(attrs1.intended_interface_addr,
			attrs2.intended_interface_addr, 6));

	assert(!memcmp(attrs1.operating_channel.country,
			attrs2.operating_channel.country, 3));
	assert(attrs1.operating_channel.oper_class ==
		attrs2.operating_channel.oper_class);
	assert(attrs1.operating_channel.channel_num ==
		attrs2.operating_channel.channel_num);

	assert(!memcmp(attrs1.channel_list.country,
			attrs2.channel_list.country, 3));
	assert(l_queue_length(attrs1.channel_list.channel_entries) ==
		l_queue_length(attrs2.channel_list.channel_entries));

	assert(attrs1.connection_capability == attrs2.connection_capability);

	assert(attrs1.advertisement_id.advertisement_id ==
		attrs2.advertisement_id.advertisement_id);
	assert(!memcmp(attrs1.advertisement_id.service_mac_addr,
			attrs2.advertisement_id.service_mac_addr, 6));

	assert(attrs1.config_timeout.go_config_timeout ==
		attrs2.config_timeout.go_config_timeout);
	assert(attrs1.config_timeout.client_config_timeout ==
		attrs2.config_timeout.client_config_timeout);

	assert(attrs1.session_id.session_id == attrs2.session_id.session_id);
	assert(!memcmp(attrs1.session_id.session_mac_addr,
			attrs2.session_id.session_mac_addr, 6));

	assert(attrs1.transport_protocol == attrs2.transport_protocol);

	assert(!memcmp(attrs1.persistent_group_info.device_addr,
			attrs2.persistent_group_info.device_addr, 6));
	assert(!strcmp(attrs1.persistent_group_info.ssid,
			attrs2.persistent_group_info.ssid));

	assert(attrs1.session_info.data_len == attrs2.session_info.data_len);
	assert(!attrs1.session_info.data_len ||
		!memcmp(attrs1.session_info.data, attrs2.session_info.data,
			attrs1.session_info.data_len));

	assert(attrs1.wsc_config_method == attrs2.wsc_config_method);

	p2p_clear_provision_disc_resp(&attrs1);
	p2p_clear_provision_disc_resp(&attrs2);
}

int main(int argc, char *argv[])
{
	l_test_init(&argc, &argv);

	l_test_add("/p2p/iter/sanity-check", p2p_test_iter_sanity_check, NULL);

	l_test_add("/p2p/parse/Beacon IEs 1", p2p_test_parse_beacon,
			&p2p_beacon_data_1);

	l_test_add("/p2p/build/Beacon IEs 1", p2p_test_build_beacon,
			&p2p_beacon_data_1);

	l_test_add("/p2p/parse/Probe Request IEs 1", p2p_test_parse_probe_req,
			&p2p_probe_req_data_1);

	l_test_add("/p2p/build/Probe Request IEs 1", p2p_test_build_probe_req,
			&p2p_probe_req_data_1);

	l_test_add("/p2p/parse/Probe Response IEs 1", p2p_test_parse_probe_resp,
			&p2p_probe_resp_data_1);
	l_test_add("/p2p/parse/Probe Response IEs 2", p2p_test_parse_probe_resp,
			&p2p_probe_resp_data_2);

	l_test_add("/p2p/build/Probe Response IEs 1", p2p_test_build_probe_resp,
			&p2p_probe_resp_data_1);
	l_test_add("/p2p/build/Probe Response IEs 2", p2p_test_build_probe_resp,
			&p2p_probe_resp_data_2);

	l_test_add("/p2p/parse/Association Request IEs 1",
			p2p_test_parse_association_req,
			&p2p_association_req_data_1);

	l_test_add("/p2p/build/Association Request IEs 1",
			p2p_test_build_association_req,
			&p2p_association_req_data_1);

	l_test_add("/p2p/parse/Association Response IEs 1",
			p2p_test_parse_association_resp,
			&p2p_association_resp_data_1);

	l_test_add("/p2p/build/Association Response IEs 1",
			p2p_test_build_association_resp,
			&p2p_association_resp_data_1);

	l_test_add("/p2p/parse/Provision Discovery Request 1",
			p2p_test_parse_provision_disc_req,
			&p2p_provision_disc_req_data_1);

	l_test_add("/p2p/build/Provision Discovery Request 1",
			p2p_test_build_provision_disc_req,
			&p2p_provision_disc_req_data_1);

	l_test_add("/p2p/parse/Provision Discovery Response 1",
			p2p_test_parse_provision_disc_resp,
			&p2p_provision_disc_resp_data_1);

	return l_test_run();
}
