/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Copyright (c) 2019 Martin Wilck, SUSE Linux GmbH, Nuremberg */

#define _GNU_SOURCE
#include <sys/types.h>
#include <regex.h>
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <setjmp.h>
#include <cmocka.h>
#include <scsi/sg.h>
#include "unaligned.h"
#include "debug.h"
#include "vector.h"
#include "structs.h"
#include "discovery.h"
#include "globals.c"

#define VPD_BUFSIZ 4096

struct vpdtest {
	unsigned char vpdbuf[VPD_BUFSIZ];
	char wwid[WWID_SIZE];
};

static regex_t space_re;
static int setup(void **state)
{
	struct vpdtest *vt = malloc(sizeof(*vt));
	int rc;

	if (vt == NULL)
		return -1;
	*state = vt;
	rc = regcomp(&space_re, " +", REG_EXTENDED);
	assert_int_equal(rc, 0);
	return 0;
}

static int teardown(void **state)
{
	struct vpdtest *vt = *state;

	free(vt);
	*state = NULL;
	regfree(&space_re);
	return 0;
}

/* vendor_id should have less than 8 chars to test space handling */
static const char vendor_id[] = "Linux";
static const char test_id[] =
	"A123456789AbcDefB123456789AbcDefC123456789AbcDefD123456789AbcDef";

int __wrap_ioctl(int fd, unsigned long request, void *param)
{
	int len;
	struct sg_io_hdr *io_hdr;
	unsigned char *val;

	len = mock();
	io_hdr = (struct sg_io_hdr *)param;
	assert_in_range(len, 0, io_hdr->dxfer_len);
	val = mock_ptr_type(unsigned char *);
	io_hdr->status = 0;
	memcpy(io_hdr->dxferp, val, len);
	return 0;
}


/**
 * create_vpd80() - create a "unit serial number" VPD page.
 * @buf:	VPD buffer
 * @bufsiz:	length of VPD buffer
 * @id:		input ID
 * @size:	value for the "page length" field
 * @len:	actual number of characters to use from @id
 *
 * If len < size, the content will be right aligned, as mandated by the
 * SPC spec.
 *
 * Return:	VPD length.
 */
static int create_vpd80(unsigned char *buf, size_t bufsiz, const char *id,
			int size, int len)
{
	assert_true(len <= size);

	memset(buf, 0, bufsiz);
	buf[1] = 0x80;
	put_unaligned_be16(size, buf + 2);

	memset(buf + 4, ' ', size - len);
	memcpy(buf + 4 + size - len, id, len);
	return size + 4;
}

static int _hex2bin(const char hx)
{
	assert_true(isxdigit(hx));
	if (hx >= '0' && hx <= '9')
		return hx - '0';
	if (hx >= 'a' && hx <= 'f')
		return hx - 'a' + 10;
	if (hx >= 'A' && hx <= 'F')
		return hx - 'A' + 10;
	return -1;
}

static void hex2bin(unsigned char *dst, const char *src,
		    size_t dstlen, size_t srclen)
{
	const char *sc;
	unsigned char *ds;

	assert_true(srclen % 2 == 0);
	for (sc = src, ds = dst;
	     sc < src + srclen &&  ds < dst + dstlen;
	     sc += 2, ++ds)
		*ds = 16 * _hex2bin(sc[0]) + _hex2bin(sc[1]);
}

/**
 * create_t10_vendor_id_desc() - Create T10 vendor ID
 * @desc:	descriptor buffer
 * @id:		input ID
 * @n:		number of characters to use for ID
 *		(includes 8 bytes for vendor ID!)
 *
 * Create a "T10 vendor specific ID" designation descriptor.
 * The vendor field (8 bytes) is filled with vendor_id (above).
 *
 * Return:	descriptor length.
 */
static int create_t10_vendor_id_desc(unsigned char *desc,
				     const char *id, size_t n)
{
	int vnd_len = sizeof(vendor_id) - 1;

	/* code set: ascii */
	desc[0] = 2;
	/* type: 10 vendor ID */
	desc[1] = 1;
	desc[2] = 0;
	desc[3] = n;

	memcpy(desc + 4, (const unsigned char *)vendor_id, vnd_len);
	memset(desc + 4 + vnd_len, ' ', 8 - vnd_len);
	memcpy(desc + 4 + 8, (const unsigned char *)id, n - 8);

	return n + 4;
}

/**
 * create_eui64_desc() - create EUI64 descriptor.
 * @desc, @id:	see above.
 * @n:		number of bytes (8, 12, or 16).
 *
 * Create an EUI64 designation descriptor.
 *
 * Return:	descriptor length.
 */
static int create_eui64_desc(unsigned char *desc,
				 const char *id, size_t n)
{
	assert_true(n == 8 || n == 12 || n == 16);

	/* code set: binary */
	desc[0] = 1;
	/* type: EUI64 */
	desc[1] = 2;
	desc[2] = 0;
	desc[3] = n;

	hex2bin(desc + 4, id, n, 2 * n);
	return n + 4;
}

/**
 * create_naa_desc() - create an NAA designation descriptor
 * @desc, @id:	see above.
 * @naa:	Name Address Authority field (2, 3, 5, or 6).
 *
 * Return:	descriptor length.
 */
static int create_naa_desc(unsigned char *desc,
			       const char *id, int naa)
{
	assert_true(naa == 2 || naa == 3 || naa == 5 || naa == 6);

	/* code set: binary */
	desc[0] = 1;
	/* type: NAA */
	desc[1] = 3;
	desc[2] = 0;
	desc[4] = _hex2bin(id[0]) | (naa << 4);
	switch (naa) {
	case 2:
	case 3:
	case 5:
		hex2bin(desc + 5, id + 1, 7, 14);
		desc[3] = 8;
		return 12;
	case 6:
		hex2bin(desc + 5, id + 1, 15, 30);
		desc[3] = 16;
		return 20;
	default:
		return 0;
	}
}

/* type and flags for SCSI name string designation descriptor */
enum {
	STR_EUI = 0,
	STR_NAA,
	STR_IQN,
	STR_MASK = 0xf,
	ZERO_LAST = 0x10,  /* flag to zero out some bytes at string end */
};

static const char * const str_prefix[] = {
	[STR_EUI] = "eui.",
	[STR_NAA] = "naa.",
	[STR_IQN] = "iqn.",
};

static const char byte0[] = {
	[STR_EUI] = '2',
	[STR_NAA] = '3',
	[STR_IQN] = '8',
};

/**
 * create_scsi_string_desc() - create a SCSI name string descriptor.
 * @desc, @id:	see above.
 * @typ:	one of STR_EUI, STR_NAA, STR_IQN, possibly ORd with ZERO_LAST
 * @maxlen:	number of characters to use from input ID.
 *
 * If ZERO_LAST is set, zero out the last byte.
 *
 * Return:	descriptor length.
 */
static int create_scsi_string_desc(unsigned char *desc,
				   const char *id, int typ, int maxlen)
{
	int len, plen;
	int type = typ & STR_MASK;

	/* code set: UTF-8 */
	desc[0] = 3;
	/* type: SCSI string */
	desc[1] = 8;
	desc[2] = 0;

	assert_in_range(type, STR_EUI, STR_IQN);
	assert_true(maxlen % 4 == 0);
	len = snprintf((char *)(desc + 4), maxlen, "%s%s",
		       str_prefix[type], id);
	if (len > maxlen)
		len = maxlen;
	/* zero-pad */
	if (typ & ZERO_LAST)
		len -= 2;
	plen = 4 * ((len - 1) / 4) + 4;
	memset(desc + 4 + len, '\0', plen - len);
	desc[3] = plen;
	return plen + 4;
}

/**
 * create_vpd83() - create "device identification" VPD page
 * @buf, @bufsiz, @id:	see above.
 * @type:	descriptor type to use (1, 2, 3, 8)
 * @parm:	opaque parameter (e.g. means "naa" for NAA type)
 * @len:	designator length (exact meaning depends on designator type)
 *
 * Create a "device identification" VPD page with a single
 * designation descriptor.
 *
 * Return:	VPD page length.
 */
static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id,
			uint8_t type, int parm, int len)
{
	unsigned char *desc;
	int n = 0;

	/* Fill with large number, which will cause length overflow */
	memset(buf, 0xed, bufsiz);
	buf[0] = 0;
	buf[1] = 0x83;

	desc = buf + 4;
	switch (type) {
	case 1:
		n = create_t10_vendor_id_desc(desc, id, len);
		break;
	case 2:
		n = create_eui64_desc(desc, id, len);
		break;
	case 3:
		n = create_naa_desc(desc, id, parm);
		break;
	case 8:
		n = create_scsi_string_desc(desc, id, parm, len);
		break;
	default:
		break;
	}
	put_unaligned_be16(n, buf + 2);
	return n + 4;
}

/**
 * assert_correct_wwid() - test that a retrieved WWID matches expectations
 * @test:	test name
 * @expected:	expected WWID length
 * @returned:	WWID length as returned by code under test
 * @byte0, @byte1: leading chars that our code prepends to the ID
 *		(e.g. "36" for "NAA registered extended" type)
 * @lowercase:	set if lower case WWID is expected
 * @orig:	original ID string, may be longer than wwid
 * @wwid:	WWID as returned by code under test
 */
static void assert_correct_wwid(const char *test,
				int expected, int returned,
				int byte0, int byte1, bool lowercase,
				const char *orig,
				const char *wwid)
{
	int ofs = 0, i;

	condlog(2, "%s: exp/ret: %d/%d, wwid: %s", test,
		expected, returned, wwid);
	/*
	 * byte0 and byte1 are the leading chars that our code prepends
	 * to the ID to indicate the designation descriptor type, .
	 */
	if (byte0 != 0) {
		assert_int_equal(byte0, wwid[0]);
		++ofs;
	}
	if (byte1 != 0) {
		assert_int_equal(byte1, wwid[1]);
		++ofs;
	}
	/* check matching length, and length of WWID string */
	assert_int_equal(expected, returned);
	assert_int_equal(returned, strlen(wwid));
	/* check expected string value up to expected length */
	for (i = 0; i < returned - ofs; i++)
		assert_int_equal(wwid[ofs + i],
				 lowercase ? tolower(orig[i]) : orig[i]);
}

/*
 * For T10 vendor ID - replace sequences of spaces with a single underscore.
 * Use a different implementation then libmultipath, deliberately.
 */
static char *subst_spaces(const char *src)
{
	char *dst = calloc(1, strlen(src) + 1);
	char *p;
	regmatch_t match;
	int rc = 0;

	assert_non_null(dst);

	for (rc = regexec(&space_re, src, 1, &match, 0), p = dst;
	    rc == 0;
	    src += match.rm_eo, rc = regexec(&space_re, src, 1, &match, 0)) {
		memcpy(p, src, match.rm_so);
		p += match.rm_so;
		*p = '_';
		++p;
	}
	assert_int_equal(rc, REG_NOMATCH);
	strcpy(p, src);
	return dst;
}

/**
 * test_vpd_vnd_LEN_WLEN() - test code for VPD 83, T10 vendor ID
 * @LEN:	ID length in the VPD page (includes 8 byte vendor ID)
 * @WLEN:	WWID buffer size
 *
 * The input ID is modified by inserting some spaces, to be able to
 * test the handling of spaces by the code. This is relevant only for
 * a minimum input length of 24.
 * The expected result must be adjusted accordingly.
 */
#define make_test_vpd_vnd(len, wlen)					\
static void test_vpd_vnd_ ## len ## _ ## wlen(void **state)             \
{                                                                       \
	struct vpdtest *vt = *state;					\
	int n, ret, rc;							\
	int exp_len;							\
	char *exp_wwid, *exp_subst, *input;				\
									\
	input = strdup(test_id);					\
	/* 8 vendor bytes collapsed to actual vendor ID length + 1 */	\
	/* and one '1' prepended */					\
	exp_len = len - 8 + sizeof(vendor_id) + 1;			\
									\
	/* insert some spaces to test space collapsing */		\
	input[15] = input[17] = input[18] = ' ';			\
	/* adjust expectation for space treatment */			\
	/* drop char for 2nd space on offset 17/18 */			\
	if (len >= 18 + 9)						\
		--exp_len;						\
	/* drop trailing single '_' if input ends with space */		\
	if (len == 15 + 9 || len == 17 + 9 || len == 18 + 9)		\
		--exp_len;						\
	if (exp_len >= wlen)						\
		exp_len = wlen - 1;					\
	n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), input,		\
			 1, 0, len);					\
	rc = asprintf(&exp_wwid, "%s_%s", vendor_id, input);		\
	assert_int_not_equal(rc, -1);					\
	free(input);							\
	/* Replace spaces, like code under test */			\
	exp_subst = subst_spaces(exp_wwid);				\
	free(exp_wwid);							\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);		\
	assert_correct_wwid("test_vpd_vnd_" #len "_" #wlen,		\
			    exp_len, ret, '1', 0, false,		\
			    exp_subst, vt->wwid);			\
	free(exp_subst);						\
}

/**
 * test_vpd_str_TYP_LEN_WLEN() - test code for VPD 83, SCSI name string
 * @TYP:	numeric value of STR_EUI, STR_NAA, STR_IQN above
 * @LEN:	ID length the VPD page
 * @WLEN:	WWID buffer size
 */
#define make_test_vpd_str(typ, len, wlen)				\
static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \
{                                                                       \
	struct vpdtest *vt = *state;					\
	int n, ret;							\
	int exp_len;							\
	int type = typ & STR_MASK;					\
									\
	n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,	\
			 8, typ, len);					\
	exp_len = len - strlen(str_prefix[type]);			\
	if (typ & ZERO_LAST)						\
		exp_len--;						\
	if (exp_len >= wlen)						\
		exp_len = wlen - 1;					\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);		\
	assert_correct_wwid("test_vpd_str_" #typ "_" #len "_" #wlen,	\
			    exp_len, ret, byte0[type], 0,		\
			    type != STR_IQN,				\
			    test_id, vt->wwid);				\
}

/**
 * test_vpd_naa_NAA_WLEN() - test code for VPD 83 NAA designation
 * @NAA:	Network Name Authority (2, 3, 5, or 6)
 * @WLEN:	WWID buffer size
 */
#define make_test_vpd_naa(naa, wlen)					\
static void test_vpd_naa_ ## naa ## _ ## wlen(void **state)             \
{                                                                       \
	struct vpdtest *vt = *state;					\
	int n, ret;							\
	int len, exp_len;						\
									\
	switch (naa) {							\
	case 2:								\
	case 3:								\
	case 5:								\
		len = 17;						\
		break;							\
	case 6:								\
		len = 33;						\
		break;							\
	}								\
	/* returned size is always uneven */                            \
	exp_len = wlen > len ? len :					\
		wlen % 2 == 0 ? wlen - 1 : wlen - 2;			\
									\
	n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,	\
			 3, naa, 0);					\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);		\
	assert_correct_wwid("test_vpd_naa_" #naa "_" #wlen,		\
			    exp_len, ret, '3', '0' + naa, true,		\
			    test_id, vt->wwid);				\
}

/**
 * test_cpd_naa_NAA_badlen_BAD() - test detection of bad length fields
 * @NAA:	Network Name Authority (2, 3, 5, 16)
 * @BAD:        Value for designator length field
 * @ERR:        Expected error code
 */
#define make_test_vpd_naa_badlen(NAA, BAD, ERR)			\
static void test_vpd_naa_##NAA##_badlen_##BAD(void **state)	\
{								\
	struct vpdtest *vt = *state;					\
	int n, ret;							\
									\
	n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, 3, NAA, 0); \
									\
	vt->vpdbuf[7] = BAD;						\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, 40);			\
	assert_int_equal(-ret, -ERR);					\
}

/**
 * test_vpd_eui_LEN_WLEN() - test code for VPD 83, EUI64
 * @LEN:	EUI64 length (8, 12, or 16)
 * @WLEN:	WWID buffer size
 * @SML:	Use small VPD page size
 */
#define make_test_vpd_eui(len, wlen, sml)				\
static void test_vpd_eui_ ## len ## _ ## wlen ## _ ## sml(void **state)	\
{									\
	struct vpdtest *vt = *state;                                    \
	int n, ret;							\
	/* returned size is always uneven */				\
	int exp_len = wlen > 2 * len + 1 ? 2 * len + 1 :		\
		wlen % 2 == 0 ? wlen - 1 : wlen - 2;			\
									\
	n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id,	\
			 2, 0, len);					\
	if (sml) {							\
		/* overwrite the page size to DEFAULT_SGIO_LEN + 1 */	\
		put_unaligned_be16(255, vt->vpdbuf + 2);		\
		/* this causes get_vpd_sgio to do a second ioctl */	\
		will_return(__wrap_ioctl, n);				\
		will_return(__wrap_ioctl, vt->vpdbuf);			\
	}								\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, wlen);		\
	assert_correct_wwid("test_vpd_eui_" #len "_" #wlen "_" #sml,	\
			    exp_len, ret, '2', 0, true,			\
			    test_id, vt->wwid);				\
}

/**
 * test_cpd_eui_LEN_badlen_BAD() - test detection of bad length fields
 * @NAA:	correct length(8, 12, 16)
 * @BAD:        value for designator length field
 * @ERR:        expected error code
 */
#define make_test_vpd_eui_badlen(LEN, BAD, ERR)			\
static void test_vpd_eui_badlen_##LEN##_##BAD(void **state)	\
{								\
	struct vpdtest *vt = *state;					\
	int n, ret;							\
									\
	n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, 2, 0, LEN); \
									\
	vt->vpdbuf[7] = BAD;						\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x83, 0, vt->wwid, 40);			\
	assert_int_equal(ret, ERR);					\
	if (ERR >= 0)							\
		assert_correct_wwid("test_vpd_eui_badlen_"#LEN"_"#BAD,	\
			    2 * BAD + 1, ret, '2', 0, true,		\
			    test_id, vt->wwid);				\
}

/**
 * test_vpd80_SIZE_LEN_WLEN() - test code for VPD 80
 * @SIZE, @LEN:	see create_vpd80()
 * @WLEN:	WWID buffer size
 */
#define make_test_vpd80(size, len, wlen)				\
static void test_vpd80_ ## size ## _ ## len ## _ ## wlen(void **state)  \
{									\
	struct vpdtest *vt = *state;                                    \
	int n, ret;							\
	int exp_len = len > 20 ? 20 : len;				\
	char *input = strdup(test_id);					\
									\
	/* insert trailing whitespace after pos 20 */			\
	memset(input + 20, ' ', sizeof(test_id) - 20);			\
	if (exp_len >= wlen)						\
		exp_len = wlen - 1;					\
	n = create_vpd80(vt->vpdbuf, sizeof(vt->vpdbuf), input,		\
			 size, len);					\
	will_return(__wrap_ioctl, n);					\
	will_return(__wrap_ioctl, vt->vpdbuf);				\
	ret = get_vpd_sgio(10, 0x80, 0, vt->wwid, wlen);		\
	assert_correct_wwid("test_vpd80_" #size "_" #len "_" #wlen,	\
			    exp_len, ret, 0, 0, false,			\
			    input, vt->wwid);				\
	free(input);							\
}

/* VPD 80 */
/* Tests without trailing whitespace: 21 WWID bytes required */
make_test_vpd80(20, 20, 30);
make_test_vpd80(20, 20, 21);
make_test_vpd80(20, 20, 20);
make_test_vpd80(20, 20, 10);

/* Tests with 4 byte trailing whitespace: 21 WWID bytes required */
make_test_vpd80(24, 24, 30);
make_test_vpd80(24, 24, 25);
make_test_vpd80(24, 24, 24);
make_test_vpd80(24, 24, 21);
make_test_vpd80(24, 24, 20);

/* Tests with 4 byte leading whitespace: 17 WWID bytes required */
make_test_vpd80(20, 16, 30);
make_test_vpd80(20, 16, 17);
make_test_vpd80(20, 16, 16);

/* Tests with 4 byte leading whitespace: 21 WWID bytes required */
make_test_vpd80(24, 20, 21);
make_test_vpd80(24, 20, 20);

/* Tests with leading and trailing whitespace: 21 WWID bytes required */
make_test_vpd80(30, 24, 30);
make_test_vpd80(30, 24, 21);
make_test_vpd80(30, 24, 20);

/* VPD 83, T10 vendor ID */
make_test_vpd_vnd(40, 40);
make_test_vpd_vnd(40, 30);
make_test_vpd_vnd(30, 20);
make_test_vpd_vnd(29, 30);
make_test_vpd_vnd(28, 30);
make_test_vpd_vnd(27, 30); /* space at end */
make_test_vpd_vnd(26, 30); /* space at end */
make_test_vpd_vnd(25, 30);
make_test_vpd_vnd(24, 30); /* space at end */
make_test_vpd_vnd(23, 30);
make_test_vpd_vnd(24, 20);
make_test_vpd_vnd(23, 20);
make_test_vpd_vnd(22, 20);
make_test_vpd_vnd(21, 20);
make_test_vpd_vnd(20, 20);
make_test_vpd_vnd(19, 20);
make_test_vpd_vnd(20, 10);
make_test_vpd_vnd(10, 10);

/* EUI64 tests */
/* small vpd page test */
make_test_vpd_eui(8, 32, 1);
make_test_vpd_eui(12, 32, 1);
make_test_vpd_eui(16, 40, 1);

/* 64bit, WWID size: 18 */
make_test_vpd_eui(8, 32, 0);
make_test_vpd_eui(8, 18, 0);
make_test_vpd_eui(8, 17, 0);
make_test_vpd_eui(8, 16, 0);
make_test_vpd_eui(8, 10, 0);

make_test_vpd_eui_badlen(8, 8, 17);
/* Invalid entry, length overflow */
make_test_vpd_eui_badlen(8, 12, -EOVERFLOW);
make_test_vpd_eui_badlen(8, 9, -EOVERFLOW);
/* invalid entry, no length overflow, but no full next entry */
make_test_vpd_eui_badlen(8, 7, -EINVAL);
make_test_vpd_eui_badlen(8, 5, -EINVAL);
/* invalid entry, length of next one readable but too long */
make_test_vpd_eui_badlen(8, 4, -EOVERFLOW);
make_test_vpd_eui_badlen(8, 0, -EOVERFLOW);

/* 96 bit, WWID size: 26 */
make_test_vpd_eui(12, 32, 0);
make_test_vpd_eui(12, 26, 0);
make_test_vpd_eui(12, 25, 0);
make_test_vpd_eui(12, 20, 0);
make_test_vpd_eui(12, 10, 0);

make_test_vpd_eui_badlen(12, 12, 25);
make_test_vpd_eui_badlen(12, 16, -EOVERFLOW);
make_test_vpd_eui_badlen(12, 13, -EOVERFLOW);
/* invalid entry, no length overflow, but no full next entry */
make_test_vpd_eui_badlen(12, 11, -EINVAL);
make_test_vpd_eui_badlen(12, 9, -EINVAL);
/* non-fatal - valid 8-byte descriptor */
make_test_vpd_eui_badlen(12, 8, 17);
/* invalid entry, length of next one readable but too long */
make_test_vpd_eui_badlen(12, 7, -EOVERFLOW);
make_test_vpd_eui_badlen(12, 0, -EOVERFLOW);

/* 128 bit, WWID size: 34 */
make_test_vpd_eui(16, 40, 0);
make_test_vpd_eui(16, 34, 0);
make_test_vpd_eui(16, 33, 0);
make_test_vpd_eui(16, 20, 0);

make_test_vpd_eui_badlen(16, 16, 33);
make_test_vpd_eui_badlen(16, 17, -EOVERFLOW);
make_test_vpd_eui_badlen(16, 15, -EINVAL);
make_test_vpd_eui_badlen(16, 13, -EINVAL);
/* non-fatal - valid 12-byte descriptor */
make_test_vpd_eui_badlen(16, 12, 25);
/* invalid entry, length of next one readable but too long */
make_test_vpd_eui_badlen(16, 11, -EOVERFLOW);
/* non-fatal - valid 8-byte descriptor */
make_test_vpd_eui_badlen(16, 8, 17);
/* invalid entry, length of next one readable but too long */
make_test_vpd_eui_badlen(16, 7, -EOVERFLOW);
make_test_vpd_eui_badlen(16, 0, -EOVERFLOW);

/* NAA IEEE registered extended (36), WWID size: 34 */
make_test_vpd_naa(6, 40);
make_test_vpd_naa(6, 34);
make_test_vpd_naa(6, 33);
make_test_vpd_naa(6, 32);
make_test_vpd_naa(6, 20);

/* NAA IEEE registered extended with bad designator length */
make_test_vpd_naa_badlen(6, 16, 33);
/* offset overflow */
make_test_vpd_naa_badlen(6, 17, -EOVERFLOW);
/* invalid entry, no length overflow, but no full next entry */
make_test_vpd_naa_badlen(6, 15, -EINVAL);
/* invalid entry, length of next one readable but too long */
make_test_vpd_naa_badlen(6, 8, -EOVERFLOW);
make_test_vpd_naa_badlen(6, 0, -EOVERFLOW);

/* NAA IEEE registered (35), WWID size: 18 */
make_test_vpd_naa(5, 20);
make_test_vpd_naa(5, 18);
make_test_vpd_naa(5, 17);
make_test_vpd_naa(5, 16);

/* NAA IEEE registered with bad designator length */
make_test_vpd_naa_badlen(5, 8, 17);
/* offset overflow */
make_test_vpd_naa_badlen(5, 16, -EOVERFLOW);
make_test_vpd_naa_badlen(5, 9, -EOVERFLOW);
/* invalid entry, no length overflow, but no full next entry */
make_test_vpd_naa_badlen(5, 7, -EINVAL);
/* invalid entry, length of next one readable but too long */
make_test_vpd_naa_badlen(5, 4, -EOVERFLOW);
make_test_vpd_naa_badlen(5, 0, -EOVERFLOW);

/* NAA local (33), WWID size: 18 */
make_test_vpd_naa(3, 20);
make_test_vpd_naa(3, 18);
make_test_vpd_naa(3, 17);
make_test_vpd_naa(3, 16);

/* NAA IEEE extended (32), WWID size: 18 */
make_test_vpd_naa(2, 20);
make_test_vpd_naa(2, 18);
make_test_vpd_naa(2, 17);
make_test_vpd_naa(2, 16);

/* SCSI Name string: EUI64, WWID size: 17 */
make_test_vpd_str(0, 20, 18)
make_test_vpd_str(0, 20, 17)
make_test_vpd_str(0, 20, 16)
make_test_vpd_str(0, 20, 15)

/* SCSI Name string: EUI64, zero padded, WWID size: 16 */
make_test_vpd_str(16, 20, 18)
make_test_vpd_str(16, 20, 17)
make_test_vpd_str(16, 20, 16)
make_test_vpd_str(16, 20, 15)

/* SCSI Name string: NAA, WWID size: 17 */
make_test_vpd_str(1, 20, 18)
make_test_vpd_str(1, 20, 17)
make_test_vpd_str(1, 20, 16)
make_test_vpd_str(1, 20, 15)

/* SCSI Name string: NAA, zero padded, WWID size: 16 */
make_test_vpd_str(17, 20, 18)
make_test_vpd_str(17, 20, 17)
make_test_vpd_str(17, 20, 16)
make_test_vpd_str(17, 20, 15)

/* SCSI Name string: IQN, WWID size: 17 */
make_test_vpd_str(2, 20, 18)
make_test_vpd_str(2, 20, 17)
make_test_vpd_str(2, 20, 16)
make_test_vpd_str(2, 20, 15)

/* SCSI Name string: IQN, zero padded, WWID size: 16 */
make_test_vpd_str(18, 20, 18)
make_test_vpd_str(18, 20, 17)
make_test_vpd_str(18, 20, 16)
make_test_vpd_str(18, 20, 15)

static int test_vpd(void)
{
	const struct CMUnitTest tests[] = {
		cmocka_unit_test(test_vpd80_20_20_30),
		cmocka_unit_test(test_vpd80_20_20_21),
		cmocka_unit_test(test_vpd80_20_20_20),
		cmocka_unit_test(test_vpd80_20_20_10),
		cmocka_unit_test(test_vpd80_24_24_30),
		cmocka_unit_test(test_vpd80_24_24_25),
		cmocka_unit_test(test_vpd80_24_24_24),
		cmocka_unit_test(test_vpd80_24_24_21),
		cmocka_unit_test(test_vpd80_24_24_20),
		cmocka_unit_test(test_vpd80_20_16_30),
		cmocka_unit_test(test_vpd80_20_16_17),
		cmocka_unit_test(test_vpd80_20_16_16),
		cmocka_unit_test(test_vpd80_24_20_21),
		cmocka_unit_test(test_vpd80_24_20_20),
		cmocka_unit_test(test_vpd80_30_24_30),
		cmocka_unit_test(test_vpd80_30_24_21),
		cmocka_unit_test(test_vpd80_30_24_20),
		cmocka_unit_test(test_vpd_vnd_40_40),
		cmocka_unit_test(test_vpd_vnd_40_30),
		cmocka_unit_test(test_vpd_vnd_30_20),
		cmocka_unit_test(test_vpd_vnd_29_30),
		cmocka_unit_test(test_vpd_vnd_28_30),
		cmocka_unit_test(test_vpd_vnd_27_30),
		cmocka_unit_test(test_vpd_vnd_26_30),
		cmocka_unit_test(test_vpd_vnd_25_30),
		cmocka_unit_test(test_vpd_vnd_24_30),
		cmocka_unit_test(test_vpd_vnd_23_30),
		cmocka_unit_test(test_vpd_vnd_24_20),
		cmocka_unit_test(test_vpd_vnd_23_20),
		cmocka_unit_test(test_vpd_vnd_22_20),
		cmocka_unit_test(test_vpd_vnd_21_20),
		cmocka_unit_test(test_vpd_vnd_20_20),
		cmocka_unit_test(test_vpd_vnd_19_20),
		cmocka_unit_test(test_vpd_vnd_20_10),
		cmocka_unit_test(test_vpd_vnd_10_10),
		cmocka_unit_test(test_vpd_eui_8_32_1),
		cmocka_unit_test(test_vpd_eui_12_32_1),
		cmocka_unit_test(test_vpd_eui_16_40_1),
		cmocka_unit_test(test_vpd_eui_8_32_0),
		cmocka_unit_test(test_vpd_eui_8_18_0),
		cmocka_unit_test(test_vpd_eui_8_17_0),
		cmocka_unit_test(test_vpd_eui_8_16_0),
		cmocka_unit_test(test_vpd_eui_8_10_0),
		cmocka_unit_test(test_vpd_eui_badlen_8_8),
		cmocka_unit_test(test_vpd_eui_badlen_8_12),
		cmocka_unit_test(test_vpd_eui_badlen_8_9),
		cmocka_unit_test(test_vpd_eui_badlen_8_7),
		cmocka_unit_test(test_vpd_eui_badlen_8_5),
		cmocka_unit_test(test_vpd_eui_badlen_8_4),
		cmocka_unit_test(test_vpd_eui_badlen_8_0),
		cmocka_unit_test(test_vpd_eui_12_32_0),
		cmocka_unit_test(test_vpd_eui_12_26_0),
		cmocka_unit_test(test_vpd_eui_12_25_0),
		cmocka_unit_test(test_vpd_eui_12_20_0),
		cmocka_unit_test(test_vpd_eui_12_10_0),
		cmocka_unit_test(test_vpd_eui_badlen_12_12),
		cmocka_unit_test(test_vpd_eui_badlen_12_16),
		cmocka_unit_test(test_vpd_eui_badlen_12_13),
		cmocka_unit_test(test_vpd_eui_badlen_12_11),
		cmocka_unit_test(test_vpd_eui_badlen_12_9),
		cmocka_unit_test(test_vpd_eui_badlen_12_8),
		cmocka_unit_test(test_vpd_eui_badlen_12_7),
		cmocka_unit_test(test_vpd_eui_badlen_12_0),
		cmocka_unit_test(test_vpd_eui_16_40_0),
		cmocka_unit_test(test_vpd_eui_16_34_0),
		cmocka_unit_test(test_vpd_eui_16_33_0),
		cmocka_unit_test(test_vpd_eui_16_20_0),
		cmocka_unit_test(test_vpd_eui_badlen_16_16),
		cmocka_unit_test(test_vpd_eui_badlen_16_17),
		cmocka_unit_test(test_vpd_eui_badlen_16_15),
		cmocka_unit_test(test_vpd_eui_badlen_16_13),
		cmocka_unit_test(test_vpd_eui_badlen_16_12),
		cmocka_unit_test(test_vpd_eui_badlen_16_11),
		cmocka_unit_test(test_vpd_eui_badlen_16_8),
		cmocka_unit_test(test_vpd_eui_badlen_16_7),
		cmocka_unit_test(test_vpd_eui_badlen_16_0),
		cmocka_unit_test(test_vpd_naa_6_40),
		cmocka_unit_test(test_vpd_naa_6_34),
		cmocka_unit_test(test_vpd_naa_6_33),
		cmocka_unit_test(test_vpd_naa_6_32),
		cmocka_unit_test(test_vpd_naa_6_20),
		cmocka_unit_test(test_vpd_naa_6_badlen_16),
		cmocka_unit_test(test_vpd_naa_6_badlen_15),
		cmocka_unit_test(test_vpd_naa_6_badlen_8),
		cmocka_unit_test(test_vpd_naa_6_badlen_17),
		cmocka_unit_test(test_vpd_naa_6_badlen_0),
		cmocka_unit_test(test_vpd_naa_5_20),
		cmocka_unit_test(test_vpd_naa_5_18),
		cmocka_unit_test(test_vpd_naa_5_17),
		cmocka_unit_test(test_vpd_naa_5_16),
		cmocka_unit_test(test_vpd_naa_5_badlen_8),
		cmocka_unit_test(test_vpd_naa_5_badlen_7),
		cmocka_unit_test(test_vpd_naa_5_badlen_4),
		cmocka_unit_test(test_vpd_naa_5_badlen_16),
		cmocka_unit_test(test_vpd_naa_5_badlen_9),
		cmocka_unit_test(test_vpd_naa_5_badlen_0),
		cmocka_unit_test(test_vpd_naa_3_20),
		cmocka_unit_test(test_vpd_naa_3_18),
		cmocka_unit_test(test_vpd_naa_3_17),
		cmocka_unit_test(test_vpd_naa_3_16),
		cmocka_unit_test(test_vpd_naa_2_20),
		cmocka_unit_test(test_vpd_naa_2_18),
		cmocka_unit_test(test_vpd_naa_2_17),
		cmocka_unit_test(test_vpd_naa_2_16),
		cmocka_unit_test(test_vpd_str_0_20_18),
		cmocka_unit_test(test_vpd_str_0_20_17),
		cmocka_unit_test(test_vpd_str_0_20_16),
		cmocka_unit_test(test_vpd_str_0_20_15),
		cmocka_unit_test(test_vpd_str_16_20_18),
		cmocka_unit_test(test_vpd_str_16_20_17),
		cmocka_unit_test(test_vpd_str_16_20_16),
		cmocka_unit_test(test_vpd_str_16_20_15),
		cmocka_unit_test(test_vpd_str_1_20_18),
		cmocka_unit_test(test_vpd_str_1_20_17),
		cmocka_unit_test(test_vpd_str_1_20_16),
		cmocka_unit_test(test_vpd_str_1_20_15),
		cmocka_unit_test(test_vpd_str_17_20_18),
		cmocka_unit_test(test_vpd_str_17_20_17),
		cmocka_unit_test(test_vpd_str_17_20_16),
		cmocka_unit_test(test_vpd_str_17_20_15),
		cmocka_unit_test(test_vpd_str_2_20_18),
		cmocka_unit_test(test_vpd_str_2_20_17),
		cmocka_unit_test(test_vpd_str_2_20_16),
		cmocka_unit_test(test_vpd_str_2_20_15),
		cmocka_unit_test(test_vpd_str_18_20_18),
		cmocka_unit_test(test_vpd_str_18_20_17),
		cmocka_unit_test(test_vpd_str_18_20_16),
		cmocka_unit_test(test_vpd_str_18_20_15),
	};
	return cmocka_run_group_tests(tests, setup, teardown);
}

int main(void)
{
	int ret = 0;

	init_test_verbosity(-1);
	ret += test_vpd();
	return ret;
}
