nvlist pack/unpack tester
Add an nvpair_packed test command which verifies that an nvlist packed in the XDR format can be unpacked to an identical list and when -r <refdir> is specified that it matches the unpacking of a reference output. Further, if the -x option is specified it checks that the packed output is identical to a reference packed output stored in <refdir>/<case>.ref. Reference output can be generated by adding the -R option. The core set of tests is defined in `struct nvcase test_cases[]`, a statically initialized array describing nvpairs to generate. A series of macros are used to keep the verbosity under control. The main exception is embedded nvlists which are initialized in init_nvlists(). The current set is in data_type_t order and all types are coverd, but some edge cases may be unexplored, especially since only a small number of multiple-element nvlists are created. Signed-off-by: Brooks Davis <brooks.davis@sri.com>
This commit is contained in:
parent
c3b6fd3d59
commit
5204d1027a
|
@ -22,6 +22,7 @@
|
|||
/mmap_seek
|
||||
/mmap_sync
|
||||
/mmapwrite
|
||||
/nvlist_packed
|
||||
/nvlist_to_lua
|
||||
/randfree_file
|
||||
/randwritecomp
|
||||
|
|
|
@ -72,6 +72,10 @@ scripts_zfs_tests_bin_PROGRAMS += %D%/mmap_libaio
|
|||
endif
|
||||
|
||||
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/nvlist_packed
|
||||
%C%_nvlist_packed_LDADD = \
|
||||
libnvpair.la
|
||||
|
||||
scripts_zfs_tests_bin_PROGRAMS += %D%/nvlist_to_lua
|
||||
%C%_nvlist_to_lua_LDADD = \
|
||||
libzfs_core.la \
|
||||
|
|
|
@ -0,0 +1,843 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*
|
||||
* Copyright (c) 2022 SRI International
|
||||
*
|
||||
* This software was developed by SRI International and the University of
|
||||
* Cambridge Computer Laboratory (Department of Computer Science and
|
||||
* Technology) under Defense Advanced Research Projects Agency (DARPA)
|
||||
* Contract No. HR001122C0110 ("ETC").
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions
|
||||
* are met:
|
||||
* 1. Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
* SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <sys/nvpair.h>
|
||||
#include <sys/kmem.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#define NNVLISTS 4
|
||||
|
||||
#ifndef nitems
|
||||
#define nitems(x) (sizeof (x) / sizeof ((x)[0]))
|
||||
#endif
|
||||
|
||||
static int verbose;
|
||||
static bool genrefs, ref_match_exact;
|
||||
static const char *refdir;
|
||||
static int refdir_fd;
|
||||
|
||||
static int tests_run, tests_failed;
|
||||
|
||||
static bool nvlist_equal(nvlist_t *nvl_a, nvlist_t *nvl_b);
|
||||
|
||||
static void
|
||||
usage(void)
|
||||
{
|
||||
printf("usage:\n");
|
||||
printf(" nvlist_pack [options] -a\n");
|
||||
printf(" nvlist_pack [options] <case1> [<case2> [...]]\n");
|
||||
printf(" nvlist_pack [options] -l\n");
|
||||
printf("options:\n");
|
||||
printf(" -a Run all test cases\n");
|
||||
printf(" -l list test cases\n");
|
||||
printf(" -r <dir> reference directory\n");
|
||||
printf(" -R generate reference files (requires -r)\n");
|
||||
printf(" -v verbose output\n");
|
||||
printf(" -x reference buffers must match exactly\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct nvcase {
|
||||
const char *nvc_name;
|
||||
const char *nvc_nvname; /* pair name, nvc_name used if NULL */
|
||||
data_type_t nvc_type;
|
||||
int nvc_nelem;
|
||||
const void *nvc_data;
|
||||
char *nvc_failure_reason;
|
||||
};
|
||||
|
||||
#define NVCASE_SIMPLE(type, nvtype) { \
|
||||
.nvc_name = #type, \
|
||||
.nvc_type = nvtype, \
|
||||
.nvc_data = &data_##type[0], \
|
||||
}
|
||||
#define NVCASE_STRING(string) { \
|
||||
.nvc_name = "string_" string, \
|
||||
.nvc_type = DATA_TYPE_STRING, \
|
||||
.nvc_data = string, \
|
||||
}
|
||||
#define _NVCASE_ARRAY(type, nvtype, array, nelem) { \
|
||||
.nvc_name = (type), \
|
||||
.nvc_type = (nvtype), \
|
||||
.nvc_nelem = (nelem), \
|
||||
.nvc_data = (array), \
|
||||
}
|
||||
#define _NVCASE_ARRAY_ALL(type, nvtype) \
|
||||
_NVCASE_ARRAY(#type "_array", (nvtype), data_##type, \
|
||||
nitems(data_##type))
|
||||
#define _NVCASE_ARRAY_EMPTY(type, nvtype) \
|
||||
_NVCASE_ARRAY(#type "_array_empty", (nvtype), NULL, 0)
|
||||
#define _NVCASE_ARRAY_SINGLE(type, nvtype) \
|
||||
_NVCASE_ARRAY(#type "_array_single", (nvtype), data_##type, 1)
|
||||
#define NVCASE_ARRAY(type, nvtype) \
|
||||
_NVCASE_ARRAY_ALL(type, nvtype), \
|
||||
_NVCASE_ARRAY_EMPTY(type, nvtype), \
|
||||
_NVCASE_ARRAY_SINGLE(type, nvtype)
|
||||
|
||||
static boolean_t data_boolean[] = {true, false, false, true};
|
||||
static uchar_t data_byte[] = {'a', 'b', 'c', 'd'};
|
||||
static int8_t data_int8[] = {0, 1, 2, -1};
|
||||
static uint8_t data_uint8[] = {0, 1, 2, 255};
|
||||
static int16_t data_int16[] = {0, 1, 2, -1};
|
||||
static uint16_t data_uint16[] = {0, 1, 2, 255};
|
||||
static int32_t data_int32[] = {0, 1, 2, -1};
|
||||
static uint32_t data_uint32[] = {0, 1, 2, UINT_MAX};
|
||||
static int64_t data_int64[] = {0, 1, 2, -1};
|
||||
static uint64_t data_uint64[] = {0, 1, 2, ULONG_MAX};
|
||||
|
||||
static hrtime_t data_hrtime[] = {0};
|
||||
static double data_double[] = {0.0};
|
||||
|
||||
static const char *data_string[] = {"a", "quick", "brown", "fox"};
|
||||
|
||||
/*
|
||||
* Initialized by init_nvlists().
|
||||
* The naming allow the use of NVCASE_ARRAY()
|
||||
*/
|
||||
static nvlist_t _data_nvlist[NNVLISTS];
|
||||
static nvlist_t *data_nvlist[NNVLISTS];
|
||||
|
||||
static struct nvcase test_cases[] = {
|
||||
{
|
||||
.nvc_name = "boolean_flag",
|
||||
.nvc_type = DATA_TYPE_BOOLEAN,
|
||||
},
|
||||
NVCASE_SIMPLE(byte, DATA_TYPE_BYTE),
|
||||
NVCASE_SIMPLE(int16, DATA_TYPE_INT16),
|
||||
NVCASE_SIMPLE(uint16, DATA_TYPE_UINT16),
|
||||
NVCASE_SIMPLE(int32, DATA_TYPE_INT32),
|
||||
NVCASE_SIMPLE(uint32, DATA_TYPE_UINT32),
|
||||
NVCASE_SIMPLE(int64, DATA_TYPE_INT64),
|
||||
NVCASE_SIMPLE(uint64, DATA_TYPE_UINT64),
|
||||
|
||||
/* XXX: use fixed width nvnames to actually hit all aligments */
|
||||
NVCASE_STRING(""),
|
||||
NVCASE_STRING("0"),
|
||||
NVCASE_STRING("01"),
|
||||
NVCASE_STRING("012"),
|
||||
NVCASE_STRING("0123"),
|
||||
NVCASE_STRING("01234"),
|
||||
NVCASE_STRING("012345"),
|
||||
NVCASE_STRING("0123456"),
|
||||
NVCASE_STRING("01234567"),
|
||||
NVCASE_STRING("012345678"),
|
||||
NVCASE_STRING("0123456789"),
|
||||
NVCASE_STRING("0123456789a"),
|
||||
NVCASE_STRING("0123456789ab"),
|
||||
NVCASE_STRING("0123456789abc"),
|
||||
NVCASE_STRING("0123456789abcd"),
|
||||
NVCASE_STRING("0123456789abcde"),
|
||||
NVCASE_STRING("0123456789abcdef"),
|
||||
NVCASE_STRING("0123456789abcdefg"),
|
||||
|
||||
NVCASE_ARRAY(byte, DATA_TYPE_BYTE_ARRAY),
|
||||
NVCASE_ARRAY(int16, DATA_TYPE_INT16_ARRAY),
|
||||
NVCASE_ARRAY(uint16, DATA_TYPE_UINT16_ARRAY),
|
||||
NVCASE_ARRAY(int32, DATA_TYPE_INT32_ARRAY),
|
||||
NVCASE_ARRAY(uint32, DATA_TYPE_UINT32_ARRAY),
|
||||
NVCASE_ARRAY(int64, DATA_TYPE_INT64_ARRAY),
|
||||
NVCASE_ARRAY(uint64, DATA_TYPE_UINT64_ARRAY),
|
||||
NVCASE_ARRAY(string, DATA_TYPE_STRING_ARRAY),
|
||||
NVCASE_SIMPLE(hrtime, DATA_TYPE_HRTIME),
|
||||
|
||||
{
|
||||
.nvc_name = "nvlist0",
|
||||
.nvc_type = DATA_TYPE_NVLIST,
|
||||
.nvc_data = &_data_nvlist[0],
|
||||
},
|
||||
{
|
||||
.nvc_name = "nvlist1",
|
||||
.nvc_type = DATA_TYPE_NVLIST,
|
||||
.nvc_data = &_data_nvlist[1],
|
||||
},
|
||||
{
|
||||
.nvc_name = "nvlist2",
|
||||
.nvc_type = DATA_TYPE_NVLIST,
|
||||
.nvc_data = &_data_nvlist[2],
|
||||
},
|
||||
{
|
||||
.nvc_name = "nvlist3",
|
||||
.nvc_type = DATA_TYPE_NVLIST,
|
||||
.nvc_data = &_data_nvlist[3],
|
||||
},
|
||||
NVCASE_ARRAY(nvlist, DATA_TYPE_NVLIST_ARRAY),
|
||||
|
||||
NVCASE_SIMPLE(boolean, DATA_TYPE_BOOLEAN_VALUE),
|
||||
NVCASE_SIMPLE(int8, DATA_TYPE_INT8),
|
||||
NVCASE_SIMPLE(uint8, DATA_TYPE_UINT8),
|
||||
NVCASE_ARRAY(boolean, DATA_TYPE_BOOLEAN_ARRAY),
|
||||
NVCASE_ARRAY(int8, DATA_TYPE_INT8_ARRAY),
|
||||
NVCASE_ARRAY(uint8, DATA_TYPE_UINT8_ARRAY),
|
||||
NVCASE_SIMPLE(double, DATA_TYPE_DOUBLE),
|
||||
|
||||
{
|
||||
.nvc_name = "empty_name",
|
||||
.nvc_nvname = "",
|
||||
.nvc_type = DATA_TYPE_BOOLEAN,
|
||||
},
|
||||
};
|
||||
|
||||
static void
|
||||
init_nvlists(void)
|
||||
{
|
||||
nvlist_t *nvp;
|
||||
|
||||
for (int i = 0; i < NNVLISTS; i++) {
|
||||
nvp = fnvlist_alloc();
|
||||
fnvlist_add_int32(nvp, "index", i);
|
||||
|
||||
switch (i) {
|
||||
case 0:
|
||||
fnvlist_add_byte(nvp, "byte", 'b');
|
||||
fnvlist_add_uint32(nvp, "uint32", UINT_MAX);
|
||||
fnvlist_add_int64(nvp, "int64", -1);
|
||||
fnvlist_add_string(nvp, "string", "value");
|
||||
break;
|
||||
case 1:
|
||||
fnvlist_add_nvlist(nvp, "nvlist0", data_nvlist[0]);
|
||||
break;
|
||||
case 2:
|
||||
fnvlist_add_nvlist(nvp, "nvlist1", data_nvlist[1]);
|
||||
break;
|
||||
case 3:
|
||||
fnvlist_add_nvlist(nvp, "nvlist2", data_nvlist[2]);
|
||||
break;
|
||||
default:
|
||||
abort();
|
||||
}
|
||||
|
||||
data_nvlist[i] = nvp;
|
||||
/*
|
||||
* Hack to allow statically allocated nvlist storage
|
||||
* Ideally nvlist_init() would be exposed and be able to
|
||||
* alloc programmer managed storage, but it isn't so we
|
||||
* cheat and copy the allocated one's contents into
|
||||
* static storage to allow test_cases[] to be
|
||||
* initialised at compile time.
|
||||
*/
|
||||
memcpy(&_data_nvlist[i], nvp, sizeof (_data_nvlist[i]));
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
list_tests(void)
|
||||
{
|
||||
for (int i = 0; i < nitems(test_cases); i++)
|
||||
printf("'%s'\n", test_cases[i].nvc_name);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
static int
|
||||
case_populate_nvlist(struct nvcase *tc, nvlist_t *nvl)
|
||||
{
|
||||
const char *name = tc->nvc_nvname != NULL? tc->nvc_nvname :
|
||||
tc->nvc_name;
|
||||
|
||||
switch (tc->nvc_type) {
|
||||
case DATA_TYPE_BOOLEAN:
|
||||
return (nvlist_add_boolean(nvl, name));
|
||||
case DATA_TYPE_BOOLEAN_VALUE:
|
||||
return (nvlist_add_boolean_value(nvl, name,
|
||||
*(boolean_t *)tc->nvc_data));
|
||||
case DATA_TYPE_BYTE:
|
||||
return (nvlist_add_byte(nvl, name,
|
||||
*(uchar_t *)tc->nvc_data));
|
||||
case DATA_TYPE_INT8 :
|
||||
return (nvlist_add_int8(nvl, name,
|
||||
*(int8_t *)tc->nvc_data));
|
||||
case DATA_TYPE_UINT8:
|
||||
return (nvlist_add_uint8(nvl, name,
|
||||
*(uint8_t *)tc->nvc_data));
|
||||
case DATA_TYPE_INT16:
|
||||
return (nvlist_add_int16(nvl, name,
|
||||
*(int16_t *)tc->nvc_data));
|
||||
case DATA_TYPE_UINT16:
|
||||
return (nvlist_add_uint16(nvl, name,
|
||||
*(uint16_t *)tc->nvc_data));
|
||||
case DATA_TYPE_INT32:
|
||||
return (nvlist_add_int32(nvl, name,
|
||||
*(int32_t *)tc->nvc_data));
|
||||
case DATA_TYPE_UINT32:
|
||||
return (nvlist_add_uint32(nvl, name,
|
||||
*(uint32_t *)tc->nvc_data));
|
||||
case DATA_TYPE_INT64:
|
||||
return (nvlist_add_int64(nvl, name,
|
||||
*(int64_t *)tc->nvc_data));
|
||||
case DATA_TYPE_UINT64:
|
||||
return (nvlist_add_uint64(nvl, name,
|
||||
*(uint64_t *)tc->nvc_data));
|
||||
case DATA_TYPE_HRTIME:
|
||||
return (nvlist_add_hrtime(nvl, name,
|
||||
*(hrtime_t *)tc->nvc_data));
|
||||
case DATA_TYPE_DOUBLE:
|
||||
return (nvlist_add_double(nvl, name,
|
||||
*(double *)tc->nvc_data));
|
||||
|
||||
case DATA_TYPE_BOOLEAN_ARRAY:
|
||||
return (nvlist_add_boolean_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_BYTE_ARRAY:
|
||||
return (nvlist_add_byte_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_INT8_ARRAY:
|
||||
return (nvlist_add_int8_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_UINT8_ARRAY:
|
||||
return (nvlist_add_uint8_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_INT16_ARRAY:
|
||||
return (nvlist_add_int16_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_UINT16_ARRAY:
|
||||
return (nvlist_add_uint16_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_INT32_ARRAY:
|
||||
return (nvlist_add_int32_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_UINT32_ARRAY:
|
||||
return (nvlist_add_uint32_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_INT64_ARRAY:
|
||||
return (nvlist_add_int64_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
case DATA_TYPE_UINT64_ARRAY:
|
||||
return (nvlist_add_uint64_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
|
||||
case DATA_TYPE_STRING:
|
||||
return (nvlist_add_string(nvl, name,
|
||||
tc->nvc_data));
|
||||
case DATA_TYPE_STRING_ARRAY:
|
||||
return (nvlist_add_string_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
|
||||
case DATA_TYPE_NVLIST:
|
||||
return (nvlist_add_nvlist(nvl, name,
|
||||
tc->nvc_data));
|
||||
case DATA_TYPE_NVLIST_ARRAY:
|
||||
return (nvlist_add_nvlist_array(nvl, name,
|
||||
tc->nvc_data, tc->nvc_nelem));
|
||||
default:
|
||||
return (-1);
|
||||
}
|
||||
}
|
||||
|
||||
static nvlist_t *
|
||||
case_create_nvlist(struct nvcase *tc)
|
||||
{
|
||||
nvlist_t *nvl;
|
||||
|
||||
if (nvlist_alloc(&nvl, 0, 0) != 0)
|
||||
return (NULL);
|
||||
|
||||
if (case_populate_nvlist(tc, nvl) != 0)
|
||||
return (NULL);
|
||||
|
||||
return (nvl);
|
||||
}
|
||||
|
||||
static void
|
||||
case_failed(struct nvcase *tc, const char *reason)
|
||||
{
|
||||
if (tc->nvc_failure_reason != NULL)
|
||||
return; /* Already called */
|
||||
tests_failed++;
|
||||
tc->nvc_failure_reason = strdup(reason);
|
||||
printf("FAIL: %s: %s\n", tc->nvc_name, reason);
|
||||
}
|
||||
|
||||
static void
|
||||
run_case(struct nvcase *tc)
|
||||
{
|
||||
nvlist_t *created_nvl, *ref_nvl, *unpacked_nvl;
|
||||
char *packed_buffer = NULL;
|
||||
size_t buflen;
|
||||
|
||||
tests_run++;
|
||||
|
||||
created_nvl = case_create_nvlist(tc);
|
||||
if (created_nvl == NULL) {
|
||||
case_failed(tc, "case_create_nvlist");
|
||||
return;
|
||||
}
|
||||
if (nvlist_pack(created_nvl, &packed_buffer, &buflen, NV_ENCODE_XDR,
|
||||
KM_SLEEP) != 0) {
|
||||
case_failed(tc, "nvlist_pack");
|
||||
return;
|
||||
}
|
||||
if (nvlist_unpack(packed_buffer, buflen, &unpacked_nvl,
|
||||
KM_SLEEP) != 0) {
|
||||
case_failed(tc, "nvlist_unpack (round-trip)");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nvlist_equal(created_nvl, unpacked_nvl)) {
|
||||
case_failed(tc,
|
||||
"create and unpacked nvlists aren't equal");
|
||||
return;
|
||||
}
|
||||
|
||||
if (refdir != NULL) {
|
||||
struct stat sb;
|
||||
char *ref_buffer, *ref_file;
|
||||
size_t ref_len;
|
||||
int ref_fd;
|
||||
|
||||
if (asprintf(&ref_file, "%s.ref", tc->nvc_name) == -1) {
|
||||
case_failed(tc, "asprintf");
|
||||
return;
|
||||
}
|
||||
if (genrefs) {
|
||||
ref_fd = openat(refdir_fd, ref_file,
|
||||
O_WRONLY | O_CREAT | O_TRUNC, 0660);
|
||||
if (ref_fd == -1) {
|
||||
fprintf(stderr,
|
||||
"%s: unable to create ref file %s/%s\n",
|
||||
tc->nvc_name, refdir, ref_file);
|
||||
exit(1);
|
||||
}
|
||||
if (write(ref_fd, packed_buffer, buflen) != buflen) {
|
||||
fprintf(stderr, "%s: failed to write packed\n",
|
||||
tc->nvc_name);
|
||||
exit(1);
|
||||
}
|
||||
close(ref_fd);
|
||||
}
|
||||
|
||||
ref_fd = openat(refdir_fd, ref_file, O_RDONLY);
|
||||
if (ref_fd == -1) {
|
||||
case_failed(tc, "failed to open ref file");
|
||||
return;
|
||||
}
|
||||
fstat(ref_fd, &sb);
|
||||
ref_len = sb.st_size;
|
||||
if (ref_len != buflen) {
|
||||
case_failed(tc,
|
||||
"ref_len and buflen aren't the same size");
|
||||
close(ref_fd);
|
||||
return;
|
||||
}
|
||||
ref_buffer = malloc(ref_len);
|
||||
if (read(ref_fd, ref_buffer, ref_len) != sb.st_size) {
|
||||
case_failed(tc, "failed to read from ref file");
|
||||
close(ref_fd);
|
||||
return;
|
||||
}
|
||||
close(ref_fd);
|
||||
|
||||
if (nvlist_unpack(ref_buffer, buflen, &ref_nvl,
|
||||
KM_SLEEP) != 0) {
|
||||
case_failed(tc, "nvlist_unpack (ref)");
|
||||
return;
|
||||
}
|
||||
if (!nvlist_equal(created_nvl, ref_nvl)) {
|
||||
case_failed(tc,
|
||||
"created and ref_unpacked nvlists aren't equal");
|
||||
return;
|
||||
}
|
||||
if (ref_match_exact &&
|
||||
memcmp(packed_buffer, ref_buffer, buflen) != 0) {
|
||||
case_failed(tc, "packed and ref buffers differ");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
printf("PASS: %s\n", tc->nvc_name);
|
||||
}
|
||||
|
||||
static void
|
||||
run_case_name(const char *name)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < nitems(test_cases); i++) {
|
||||
if (strcmp(name, test_cases[i].nvc_name) == 0) {
|
||||
run_case(&test_cases[i]);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fprintf(stderr, "unknown test: '%s'\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
/* CSTYLED */
|
||||
#define NVP_EQUAL_TYPE(type, name) __extension__({ \
|
||||
bool is_equal; \
|
||||
type a, b; \
|
||||
nvpair_value_##name(nvp_a, &a); \
|
||||
nvpair_value_##name(nvp_b, &b); \
|
||||
is_equal = (a == b); \
|
||||
is_equal; \
|
||||
})
|
||||
|
||||
/* CSTYLED */
|
||||
#define NVP_EQUAL_TYPE_ARRAY(type, name) __extension__({ \
|
||||
bool is_equal; \
|
||||
type *a, *b; \
|
||||
uint_t nelem_a, nelem_b; \
|
||||
nvpair_value_##name##_array(nvp_a, &a, &nelem_a); \
|
||||
nvpair_value_##name##_array(nvp_b, &b, &nelem_b); \
|
||||
is_equal = (nelem_a == nelem_b); \
|
||||
if (is_equal) \
|
||||
for (uint_t i = 0; i < nelem_a; i++) \
|
||||
if (a[i] != b[i]) { \
|
||||
is_equal = false; \
|
||||
break; \
|
||||
} \
|
||||
is_equal; \
|
||||
})
|
||||
|
||||
static bool
|
||||
nvpair_value_equal(nvpair_t *nvp_a, nvpair_t *nvp_b)
|
||||
{
|
||||
if (nvpair_type(nvp_a) != nvpair_type(nvp_b)) {
|
||||
if (verbose >= 2)
|
||||
printf("%s: pair types differ\n", __func__);
|
||||
return (false);
|
||||
}
|
||||
switch (nvpair_type(nvp_a)) {
|
||||
case DATA_TYPE_BOOLEAN:
|
||||
return (true); /* Presence is the value */
|
||||
|
||||
case DATA_TYPE_BOOLEAN_VALUE:
|
||||
if (NVP_EQUAL_TYPE(boolean_t, boolean_value))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_BYTE:
|
||||
if (NVP_EQUAL_TYPE(uchar_t, byte))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT8 :
|
||||
if (NVP_EQUAL_TYPE(int8_t, int8))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT8:
|
||||
if (NVP_EQUAL_TYPE(uint8_t, uint8))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT16:
|
||||
if (NVP_EQUAL_TYPE(int16_t, int16))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT16:
|
||||
if (NVP_EQUAL_TYPE(uint16_t, uint16))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT32:
|
||||
if (NVP_EQUAL_TYPE(int32_t, int32))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT32:
|
||||
if (NVP_EQUAL_TYPE(uint32_t, uint32))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT64:
|
||||
if (NVP_EQUAL_TYPE(int64_t, int64))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT64:
|
||||
if (NVP_EQUAL_TYPE(uint64_t, uint64))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_HRTIME:
|
||||
if (NVP_EQUAL_TYPE(hrtime_t, hrtime))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_DOUBLE:
|
||||
if (NVP_EQUAL_TYPE(double, double))
|
||||
return (true);
|
||||
break;
|
||||
|
||||
case DATA_TYPE_BOOLEAN_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(boolean_t, boolean))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_BYTE_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(uchar_t, byte))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT8_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(int8_t, int8))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT8_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(uint8_t, uint8))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT16_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(int16_t, int16))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT16_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(uint16_t, uint16))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT32_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(int32_t, int32))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT32_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(uint32_t, uint32))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_INT64_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(int64_t, int64))
|
||||
return (true);
|
||||
break;
|
||||
case DATA_TYPE_UINT64_ARRAY:
|
||||
if (NVP_EQUAL_TYPE_ARRAY(uint64_t, uint64))
|
||||
return (true);
|
||||
break;
|
||||
|
||||
case DATA_TYPE_STRING: {
|
||||
char *str_a, *str_b;
|
||||
nvpair_value_string(nvp_a, &str_a);
|
||||
nvpair_value_string(nvp_b, &str_b);
|
||||
if (strcmp(str_a, str_b) == 0)
|
||||
return (true);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_STRING_ARRAY: {
|
||||
char **stra_a, **stra_b;
|
||||
uint_t nelem_a, nelem_b;
|
||||
nvpair_value_string_array(nvp_a, &stra_a, &nelem_a);
|
||||
nvpair_value_string_array(nvp_b, &stra_b, &nelem_b);
|
||||
if (nelem_a != nelem_b)
|
||||
goto not_equal;
|
||||
for (int i = 0; i < nelem_a; i++) {
|
||||
if (strcmp(stra_a[i], stra_b[i]) != 0)
|
||||
goto not_equal;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
case DATA_TYPE_NVLIST: {
|
||||
nvlist_t *nvl_a, *nvl_b;
|
||||
nvpair_value_nvlist(nvp_a, &nvl_a);
|
||||
nvpair_value_nvlist(nvp_b, &nvl_b);
|
||||
if (nvlist_equal(nvl_a, nvl_b))
|
||||
return (true);
|
||||
break;
|
||||
}
|
||||
case DATA_TYPE_NVLIST_ARRAY: {
|
||||
nvlist_t **nvla_a, **nvla_b;
|
||||
uint_t nelem_a, nelem_b;
|
||||
nvpair_value_nvlist_array(nvp_a, &nvla_a, &nelem_a);
|
||||
nvpair_value_nvlist_array(nvp_b, &nvla_b, &nelem_b);
|
||||
if (nelem_a != nelem_b)
|
||||
goto not_equal;
|
||||
for (int i = 0; i < nelem_a; i++) {
|
||||
if (!nvlist_equal(nvla_a[i], nvla_b[i]))
|
||||
goto not_equal;
|
||||
}
|
||||
return (true);
|
||||
}
|
||||
|
||||
case DATA_TYPE_DONTCARE:
|
||||
case DATA_TYPE_UNKNOWN:
|
||||
if (verbose >= 2)
|
||||
printf("%s: unhandled type %d\n", __func__,
|
||||
nvpair_type(nvp_a));
|
||||
return (false);
|
||||
}
|
||||
|
||||
not_equal:
|
||||
if (verbose >= 2)
|
||||
printf("%s: values are not equal\n", __func__);
|
||||
return (false);
|
||||
}
|
||||
#undef NVP_EQUAL_TYPE
|
||||
#undef NVP_EQUAL_TYPE_ARRAY
|
||||
|
||||
static bool
|
||||
nvpair_equal(nvpair_t *nvp_a, nvpair_t *nvp_b)
|
||||
{
|
||||
if (strcmp(nvpair_name(nvp_a), nvpair_name(nvp_b)) != 0) {
|
||||
if (verbose >= 2)
|
||||
printf("%s: pair names differ\n", __func__);
|
||||
return (false);
|
||||
}
|
||||
return (nvpair_value_equal(nvp_a, nvp_b));
|
||||
}
|
||||
|
||||
/*
|
||||
* nvlist_equal - check if two nvlists are equal.
|
||||
*
|
||||
* Each pair be present in each list and they must appear in the same
|
||||
* order. While ordering does not matter from an API perspective, it
|
||||
* must hold for the packed forms to be identical.
|
||||
*/
|
||||
static bool
|
||||
nvlist_equal(nvlist_t *nvl_a, nvlist_t *nvl_b)
|
||||
{
|
||||
nvpair_t *nvp_a, *nvp_b;
|
||||
|
||||
if (fnvlist_num_pairs(nvl_a) != fnvlist_num_pairs(nvl_b))
|
||||
return (false);
|
||||
|
||||
if (verbose >= 3) {
|
||||
nvpair_t *pair;
|
||||
pair = NULL;
|
||||
printf("dumping nvp_a\n");
|
||||
while ((pair = nvlist_next_nvpair(nvl_a, pair)) != NULL)
|
||||
printf("'%s'\n", nvpair_name(pair));
|
||||
printf("dumping nvp_b\n");
|
||||
while ((pair = nvlist_next_nvpair(nvl_b, pair)) != NULL)
|
||||
printf("'%s'\n", nvpair_name(pair));
|
||||
}
|
||||
|
||||
for (nvp_a = nvlist_next_nvpair(nvl_a, NULL),
|
||||
nvp_b = nvlist_next_nvpair(nvl_b, NULL);
|
||||
nvp_a != NULL && nvp_b != NULL;
|
||||
nvp_a = nvlist_next_nvpair(nvl_a, nvp_a),
|
||||
nvp_b = nvlist_next_nvpair(nvl_b, nvp_b)) {
|
||||
if (!nvpair_equal(nvp_a, nvp_b))
|
||||
return (false);
|
||||
}
|
||||
if (nvp_a == NULL && nvp_b == NULL)
|
||||
return (true);
|
||||
|
||||
return (false);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
int i, opt;
|
||||
bool list, run_all;
|
||||
|
||||
list = run_all = false;
|
||||
|
||||
while ((opt = getopt(argc, argv, "alr:Rvx")) != -1) {
|
||||
switch (opt) {
|
||||
case 'a':
|
||||
run_all = true;
|
||||
break;
|
||||
case 'l':
|
||||
list = true;
|
||||
break;
|
||||
case 'r':
|
||||
refdir = optarg;
|
||||
break;
|
||||
case 'R':
|
||||
genrefs = true;
|
||||
break;
|
||||
case 'v':
|
||||
verbose++;
|
||||
break;
|
||||
case 'x':
|
||||
ref_match_exact = true;
|
||||
break;
|
||||
default:
|
||||
fprintf(stderr, "unknown argument %c\n", opt);
|
||||
usage();
|
||||
}
|
||||
}
|
||||
argc -= optind;
|
||||
argv += optind;
|
||||
if (run_all && list) {
|
||||
fprintf(stderr, "-a and -l are incompatible\n");
|
||||
usage();
|
||||
}
|
||||
if (list && genrefs) {
|
||||
fprintf(stderr, "-l and -R are incompatible\n");
|
||||
usage();
|
||||
}
|
||||
if (list && refdir != NULL) {
|
||||
fprintf(stderr, "-l and -r are incompatible\n");
|
||||
usage();
|
||||
}
|
||||
if (list) {
|
||||
if (argc == 0)
|
||||
list_tests();
|
||||
fprintf(stderr, "-l and a list of test are incompatible\n");
|
||||
usage();
|
||||
}
|
||||
if (argc == 0 && !run_all)
|
||||
usage();
|
||||
if (argc > 0 && run_all) {
|
||||
fprintf(stderr, "-a and a list of cases are incompatible\n");
|
||||
usage();
|
||||
}
|
||||
|
||||
if (refdir != NULL) {
|
||||
#ifdef O_PATH
|
||||
const int o_path_flag = O_PATH;
|
||||
#else
|
||||
const int o_path_flag = O_RDONLY;
|
||||
#endif
|
||||
|
||||
refdir_fd = open(refdir, O_DIRECTORY | O_CLOEXEC | o_path_flag);
|
||||
if (refdir_fd == -1) {
|
||||
fprintf(stderr, "Failed to open refdir %s: %s\n",
|
||||
refdir, strerror(errno));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
init_nvlists();
|
||||
|
||||
if (run_all) {
|
||||
for (i = 0; i < nitems(test_cases); i++)
|
||||
run_case(&test_cases[i]);
|
||||
} else {
|
||||
for (i = 0; i < argc; i++)
|
||||
run_case_name(argv[i]);
|
||||
}
|
||||
|
||||
if (verbose > 0 && tests_failed > 0) {
|
||||
printf("Unexpected failures:\n");
|
||||
for (i = 0; i < nitems(test_cases); i++) {
|
||||
if (test_cases[i].nvc_failure_reason != NULL) {
|
||||
printf("\t%s: %s\n", test_cases[i].nvc_name,
|
||||
test_cases[i].nvc_failure_reason);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (verbose >= 0) {
|
||||
printf("SUMMARY");
|
||||
if (tests_run - tests_failed > 0)
|
||||
printf(": passed %d", tests_run - tests_failed);
|
||||
if (tests_failed > 0)
|
||||
printf(": failed %d", tests_failed);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
return (tests_failed);
|
||||
}
|
Loading…
Reference in New Issue