tests: add test for vdev_disk page alignment check
This provides a test driver and a set of test vectors for the page alignment check callback function vdev_disk_check_pages_cb(). Because there's no good facility for exposing this function to a userspace test right now, for now I'm just duplicating the function and adding commentary to remind people to keep them in sync. Sponsored-by: Klara, Inc. Sponsored-by: Wasabi Technology, Inc. Reviewed-by: Alexander Motin <mav@FreeBSD.org> Reviewed-by: Brian Behlendorf <behlendorf1@llnl.gov> Signed-off-by: Rob Norris <rob.norris@klarasystems.com> Closes #16076
This commit is contained in:
parent
44f337be30
commit
bc27c49404
|
@ -853,6 +853,11 @@ BIO_END_IO_PROTO(vbio_completion, bio, error)
|
||||||
* pages) but we still have to ensure the data portion is correctly sized and
|
* pages) but we still have to ensure the data portion is correctly sized and
|
||||||
* aligned to the logical block size, to ensure that if the kernel wants to
|
* aligned to the logical block size, to ensure that if the kernel wants to
|
||||||
* split the BIO, the two halves will still be properly aligned.
|
* split the BIO, the two halves will still be properly aligned.
|
||||||
|
*
|
||||||
|
* NOTE: if you change this function, change the copy in
|
||||||
|
* tests/zfs-tests/tests/functional/vdev_disk/page_alignment.c, and add test
|
||||||
|
* data there to validate the change you're making.
|
||||||
|
*
|
||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint_t bmask;
|
uint_t bmask;
|
||||||
|
@ -863,6 +868,7 @@ typedef struct {
|
||||||
static int
|
static int
|
||||||
vdev_disk_check_pages_cb(struct page *page, size_t off, size_t len, void *priv)
|
vdev_disk_check_pages_cb(struct page *page, size_t off, size_t len, void *priv)
|
||||||
{
|
{
|
||||||
|
(void) page;
|
||||||
vdev_disk_check_pages_t *s = priv;
|
vdev_disk_check_pages_t *s = priv;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
@ -971,6 +971,12 @@ tests = [
|
||||||
'userspace_send_encrypted', 'userspace_encrypted_13709']
|
'userspace_send_encrypted', 'userspace_encrypted_13709']
|
||||||
tags = ['functional', 'userquota']
|
tags = ['functional', 'userquota']
|
||||||
|
|
||||||
|
[tests/functional/vdev_disk:Linux]
|
||||||
|
pre =
|
||||||
|
post =
|
||||||
|
tests = ['page_alignment']
|
||||||
|
tags = ['functional', 'vdev_disk']
|
||||||
|
|
||||||
[tests/functional/vdev_zaps]
|
[tests/functional/vdev_zaps]
|
||||||
tests = ['vdev_zaps_001_pos', 'vdev_zaps_002_pos', 'vdev_zaps_003_pos',
|
tests = ['vdev_zaps_001_pos', 'vdev_zaps_002_pos', 'vdev_zaps_003_pos',
|
||||||
'vdev_zaps_004_pos', 'vdev_zaps_005_pos', 'vdev_zaps_006_pos',
|
'vdev_zaps_004_pos', 'vdev_zaps_005_pos', 'vdev_zaps_006_pos',
|
||||||
|
|
|
@ -599,6 +599,12 @@ tags = ['functional', 'truncate']
|
||||||
tests = ['upgrade_userobj_001_pos', 'upgrade_readonly_pool']
|
tests = ['upgrade_userobj_001_pos', 'upgrade_readonly_pool']
|
||||||
tags = ['functional', 'upgrade']
|
tags = ['functional', 'upgrade']
|
||||||
|
|
||||||
|
[tests/functional/vdev_disk:Linux]
|
||||||
|
pre =
|
||||||
|
post =
|
||||||
|
tests = ['page_alignment']
|
||||||
|
tags = ['functional', 'vdev_disk']
|
||||||
|
|
||||||
[tests/functional/vdev_zaps]
|
[tests/functional/vdev_zaps]
|
||||||
tests = ['vdev_zaps_001_pos', 'vdev_zaps_003_pos', 'vdev_zaps_004_pos',
|
tests = ['vdev_zaps_001_pos', 'vdev_zaps_003_pos', 'vdev_zaps_004_pos',
|
||||||
'vdev_zaps_005_pos', 'vdev_zaps_006_pos']
|
'vdev_zaps_005_pos', 'vdev_zaps_006_pos']
|
||||||
|
|
|
@ -13,6 +13,9 @@ scripts_zfs_tests_functional_hkdf_PROGRAMS = %D%/tests/functional/hkdf/hkdf_test
|
||||||
%C%_tests_functional_hkdf_hkdf_test_LDADD = \
|
%C%_tests_functional_hkdf_hkdf_test_LDADD = \
|
||||||
libzpool.la
|
libzpool.la
|
||||||
|
|
||||||
|
scripts_zfs_tests_functional_vdev_diskdir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/vdev_disk
|
||||||
|
scripts_zfs_tests_functional_vdev_disk_PROGRAMS = %D%/tests/functional/vdev_disk/page_alignment
|
||||||
|
|
||||||
scripts_zfs_tests_functional_cp_filesdir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/cp_files
|
scripts_zfs_tests_functional_cp_filesdir = $(datadir)/$(PACKAGE)/zfs-tests/tests/functional/cp_files
|
||||||
scripts_zfs_tests_functional_cp_files_PROGRAMS = %D%/tests/functional/cp_files/seekflood
|
scripts_zfs_tests_functional_cp_files_PROGRAMS = %D%/tests/functional/cp_files/seekflood
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
page_alignment
|
|
@ -0,0 +1,413 @@
|
||||||
|
/*
|
||||||
|
* CDDL HEADER START
|
||||||
|
*
|
||||||
|
* The contents of this file are subject to the terms of the
|
||||||
|
* Common Development and Distribution License (the "License").
|
||||||
|
* You may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
|
||||||
|
* or https://opensource.org/licenses/CDDL-1.0.
|
||||||
|
* See the License for the specific language governing permissions
|
||||||
|
* and limitations under the License.
|
||||||
|
*
|
||||||
|
* When distributing Covered Code, include this CDDL HEADER in each
|
||||||
|
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
|
||||||
|
* If applicable, add the following below this CDDL HEADER, with the
|
||||||
|
* fields enclosed by brackets "[]" replaced with your own identifying
|
||||||
|
* information: Portions Copyright [yyyy] [name of copyright owner]
|
||||||
|
*
|
||||||
|
* CDDL HEADER END
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2023, 2024, Klara Inc.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <sys/param.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This tests the vdev_disk page alignment check callback
|
||||||
|
* vdev_disk_check_pages_cb(). For now, this test includes a copy of that
|
||||||
|
* function from module/os/linux/zfs/vdev_disk.c. If you change it here,
|
||||||
|
* remember to change it there too, and add tests data here to validate the
|
||||||
|
* change you're making.
|
||||||
|
*/
|
||||||
|
|
||||||
|
struct page;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
uint32_t bmask;
|
||||||
|
uint32_t npages;
|
||||||
|
uint32_t end;
|
||||||
|
} vdev_disk_check_pages_t;
|
||||||
|
|
||||||
|
static int
|
||||||
|
vdev_disk_check_pages_cb(struct page *page, size_t off, size_t len, void *priv)
|
||||||
|
{
|
||||||
|
(void) page;
|
||||||
|
vdev_disk_check_pages_t *s = priv;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If we didn't finish on a block size boundary last time, then there
|
||||||
|
* would be a gap if we tried to use this ABD as-is, so abort.
|
||||||
|
*/
|
||||||
|
if (s->end != 0)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Note if we're taking less than a full block, so we can check it
|
||||||
|
* above on the next call.
|
||||||
|
*/
|
||||||
|
s->end = len & s->bmask;
|
||||||
|
|
||||||
|
/* All blocks after the first must start on a block size boundary. */
|
||||||
|
if (s->npages != 0 && (off & s->bmask) != 0)
|
||||||
|
return (1);
|
||||||
|
|
||||||
|
s->npages++;
|
||||||
|
return (0);
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* test name */
|
||||||
|
const char *name;
|
||||||
|
|
||||||
|
/* blocks size mask */
|
||||||
|
uint32_t mask;
|
||||||
|
|
||||||
|
/* amount of data to take */
|
||||||
|
size_t size;
|
||||||
|
|
||||||
|
/* [start offset in page, len to end of page or size] */
|
||||||
|
size_t pages[16][2];
|
||||||
|
} page_test_t;
|
||||||
|
|
||||||
|
static const page_test_t valid_tests[] = {
|
||||||
|
/* 512B block tests */
|
||||||
|
{
|
||||||
|
"512B blocks, 4K single page",
|
||||||
|
0x1ff, 0x1000, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 1K at start of page",
|
||||||
|
0x1ff, 0x400, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 1K at end of page",
|
||||||
|
0x1ff, 0x400, {
|
||||||
|
{ 0x0c00, 0x0400 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 1K within page, 512B start offset",
|
||||||
|
0x1ff, 0x400, {
|
||||||
|
{ 0x0200, 0x0e00 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 8K across 2x4K pages",
|
||||||
|
0x1ff, 0x2000, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 4K across two pages, 2K start offset",
|
||||||
|
0x1ff, 0x1000, {
|
||||||
|
{ 0x0800, 0x0800 },
|
||||||
|
{ 0x0, 0x0800 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 16K across 5x4K pages, 512B start offset",
|
||||||
|
0x1ff, 0x4000, {
|
||||||
|
{ 0x0200, 0x0e00 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0200 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 64K data, 8x8K compound pages",
|
||||||
|
0x1ff, 0x10000, {
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 64K data, 9x8K compound pages, 512B start offset",
|
||||||
|
0x1ff, 0x10000, {
|
||||||
|
{ 0x0200, 0x1e00 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x0200 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 64K data, 2x16K compound pages, 8x4K pages",
|
||||||
|
0x1ff, 0x10000, {
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 64K data, mixed 4K/8K/16K pages",
|
||||||
|
0x1ff, 0x10000, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"512B blocks, 64K data, mixed 4K/8K/16K pages, 1K start offset",
|
||||||
|
0x1ff, 0x10000, {
|
||||||
|
{ 0x0400, 0x0c00 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0400 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
/* 4K block tests */
|
||||||
|
{
|
||||||
|
"4K blocks, 4K single page",
|
||||||
|
0xfff, 0x1000, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 1K at start of page",
|
||||||
|
0xfff, 0x400, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 1K at end of page",
|
||||||
|
0xfff, 0x400, {
|
||||||
|
{ 0x0c00, 0x0400 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 1K within page, 512B start offset",
|
||||||
|
0xfff, 0x400, {
|
||||||
|
{ 0x0200, 0x0e00 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 8K across 2x4K pages",
|
||||||
|
0xfff, 0x2000, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 4K across two pages, 2K start offset",
|
||||||
|
0xfff, 0x1000, {
|
||||||
|
{ 0x0800, 0x0800 },
|
||||||
|
{ 0x0, 0x0800 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 16K across 5x4K pages, 512B start offset",
|
||||||
|
0xfff, 0x4000, {
|
||||||
|
{ 0x0200, 0x0e00 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0200 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 64K data, 8x8K compound pages",
|
||||||
|
0xfff, 0x10000, {
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 64K data, 9x8K compound pages, 512B start offset",
|
||||||
|
0xfff, 0x10000, {
|
||||||
|
{ 0x0200, 0x1e00 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x0200 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 64K data, 2x16K compound pages, 8x4K pages",
|
||||||
|
0xfff, 0x10000, {
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 64K data, mixed 4K/8K/16K pages",
|
||||||
|
0xfff, 0x10000, {
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 64K data, mixed 4K/8K/16K pages, 1K start offset",
|
||||||
|
0xfff, 0x10000, {
|
||||||
|
{ 0x0400, 0x0c00 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x2000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x8000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0400 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static const page_test_t invalid_tests[] = {
|
||||||
|
{
|
||||||
|
"512B blocks, 16K data, 512 leader (gang block simulation)",
|
||||||
|
0x1ff, 0x8000, {
|
||||||
|
{ 0x0, 0x0200 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0c00 },
|
||||||
|
},
|
||||||
|
}, {
|
||||||
|
"4K blocks, 32K data, 2 incompatible spans "
|
||||||
|
"(gang abd simulation)",
|
||||||
|
0xfff, 0x8000, {
|
||||||
|
{ 0x0800, 0x0800 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0800 },
|
||||||
|
{ 0x0800, 0x0800 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x1000 },
|
||||||
|
{ 0x0, 0x0800 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ 0 },
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
run_test(const page_test_t *test, bool verbose)
|
||||||
|
{
|
||||||
|
size_t rem = test->size;
|
||||||
|
|
||||||
|
vdev_disk_check_pages_t s = {
|
||||||
|
.bmask = 0xfff,
|
||||||
|
.npages = 0,
|
||||||
|
.end = 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; test->pages[i][1] > 0; i++) {
|
||||||
|
size_t off = test->pages[i][0];
|
||||||
|
size_t len = test->pages[i][1];
|
||||||
|
|
||||||
|
size_t take = MIN(rem, len);
|
||||||
|
|
||||||
|
if (verbose)
|
||||||
|
printf(" page %d [off %lx len %lx], "
|
||||||
|
"rem %lx, take %lx\n",
|
||||||
|
i, off, len, rem, take);
|
||||||
|
|
||||||
|
if (vdev_disk_check_pages_cb(NULL, off, take, &s)) {
|
||||||
|
if (verbose)
|
||||||
|
printf(" ABORT: misalignment detected, "
|
||||||
|
"rem %lx\n", rem);
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
rem -= take;
|
||||||
|
if (rem == 0)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rem > 0) {
|
||||||
|
if (verbose)
|
||||||
|
printf(" ABORT: ran out of pages, rem %lx\n", rem);
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
run_test_set(const page_test_t *tests, bool want, int *ntests, int *npassed)
|
||||||
|
{
|
||||||
|
for (const page_test_t *test = &tests[0]; test->name; test++) {
|
||||||
|
bool pass = (run_test(test, false) == want);
|
||||||
|
if (pass) {
|
||||||
|
printf("%s: PASS\n", test->name);
|
||||||
|
(*npassed)++;
|
||||||
|
} else {
|
||||||
|
printf("%s: FAIL [expected %s, got %s]\n", test->name,
|
||||||
|
want ? "VALID" : "INVALID",
|
||||||
|
want ? "INVALID" : "VALID");
|
||||||
|
run_test(test, true);
|
||||||
|
}
|
||||||
|
(*ntests)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(void) {
|
||||||
|
int ntests = 0, npassed = 0;
|
||||||
|
|
||||||
|
run_test_set(valid_tests, true, &ntests, &npassed);
|
||||||
|
run_test_set(invalid_tests, false, &ntests, &npassed);
|
||||||
|
|
||||||
|
printf("\n%d/%d tests passed\n", npassed, ntests);
|
||||||
|
|
||||||
|
return (ntests == npassed ? 0 : 1);
|
||||||
|
}
|
Loading…
Reference in New Issue