mirror of
https://github.com/xen-project/xen.git
synced 2026-10-28 02:01:24 +01:00
tootls/tests: introduce unit tests for rangesets
Introduce some basic infrastructure for doing rangeset unit tests, and add a few tests that ensure correctness of rangeset subtraction. Signed-off-by: Roger Pau Monné <roger.pau@citrix.com> Reviewed-by: Jan Beulich <jbeulich@suse.com>
This commit is contained in:
1 parent
e118fc98e7
commit
7bf777b42c
4 files changed
+346
No files matched your search
@@ -10,6 +10,7 @@ SUBDIRS-$(CONFIG_X86) += x86_emulator
|
||||
endif
|
||||
SUBDIRS-y += xenstore
|
||||
SUBDIRS-y += depriv
|
||||
SUBDIRS-y += rangeset
|
||||
SUBDIRS-y += vpci
|
||||
SUBDIRS-y += paging-mempool
|
||||
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
XEN_ROOT=$(CURDIR)/../../..
|
||||
include $(XEN_ROOT)/tools/Rules.mk
|
||||
|
||||
TARGET := test-rangeset
|
||||
|
||||
.PHONY: all
|
||||
all: $(TARGET)
|
||||
|
||||
.PHONY: run
|
||||
run: $(TARGET)
|
||||
./$<
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
$(RM) -- *.o $(TARGET) $(DEPS_RM)
|
||||
|
||||
.PHONY: distclean
|
||||
distclean: clean
|
||||
$(RM) -- *~
|
||||
|
||||
.PHONY: install
|
||||
install: all
|
||||
$(INSTALL_DIR) $(DESTDIR)$(LIBEXEC_BIN)
|
||||
$(INSTALL_PROG) $(TARGET) $(DESTDIR)$(LIBEXEC_BIN)
|
||||
|
||||
.PHONY: uninstall
|
||||
uninstall:
|
||||
$(RM) -- $(addprefix $(DESTDIR)$(LIBEXEC_BIN)/,$(TARGET))
|
||||
|
||||
list.h: $(XEN_ROOT)/xen/include/xen/list.h
|
||||
rangeset.h: $(XEN_ROOT)/xen/include/xen/rangeset.h
|
||||
list.h rangeset.h:
|
||||
sed -e '/#include/d' <$< >$@
|
||||
|
||||
rangeset.c: $(XEN_ROOT)/xen/common/rangeset.c list.h rangeset.h
|
||||
# Remove includes and add the test harness header
|
||||
sed -e '/#include/d' -e '1s/^/#include "harness.h"/' <$< >$@
|
||||
|
||||
CFLAGS += -D__XEN_TOOLS__
|
||||
CFLAGS += $(APPEND_CFLAGS)
|
||||
CFLAGS += $(CFLAGS_xeninclude)
|
||||
|
||||
LDFLAGS += $(APPEND_LDFLAGS)
|
||||
|
||||
test-rangeset: rangeset.o test-rangeset.o
|
||||
$(CC) $^ -o $@ $(LDFLAGS)
|
||||
@@ -0,0 +1,71 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Unit tests for rangesets.
|
||||
*
|
||||
* Copyright (C) 2025 Cloud Software Group
|
||||
*/
|
||||
|
||||
#ifndef _TEST_HARNESS_
|
||||
#define _TEST_HARNESS_
|
||||
|
||||
#include <assert.h>
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <xen-tools/common-macros.h>
|
||||
|
||||
#define smp_wmb()
|
||||
#define __must_check __attribute__((__warn_unused_result__))
|
||||
#define cf_check
|
||||
|
||||
#define BUG_ON(x) assert(!(x))
|
||||
#define ASSERT(x) assert(x)
|
||||
|
||||
#include "list.h"
|
||||
#include "rangeset.h"
|
||||
|
||||
typedef bool rwlock_t;
|
||||
typedef bool spinlock_t;
|
||||
|
||||
struct domain {
|
||||
unsigned int domain_id;
|
||||
struct list_head rangesets;
|
||||
spinlock_t rangesets_lock;
|
||||
};
|
||||
|
||||
/* For rangeset_domain_{initialize,printk}() */
|
||||
#define spin_lock_init(l) (*(l) = false)
|
||||
#define spin_lock(l) (*(l) = true)
|
||||
#define spin_unlock(l) (*(l) = false)
|
||||
|
||||
/* For rangeset->lock */
|
||||
#define rwlock_init(l) (*(l) = false)
|
||||
#define read_lock(l) (*(l) = true)
|
||||
#define read_unlock(l) (*(l) = false)
|
||||
#define write_lock(l) (*(l) = true)
|
||||
#define write_unlock(l) (*(l) = false)
|
||||
|
||||
#define xmalloc(type) ((type *)malloc(sizeof(type)))
|
||||
#define xfree free
|
||||
|
||||
#define unlikely
|
||||
|
||||
#define safe_strcpy strcpy
|
||||
|
||||
#define printk printf
|
||||
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||
@@ -0,0 +1,228 @@
|
||||
/* SPDX-License-Identifier: GPL-2.0-only */
|
||||
/*
|
||||
* Unit tests for rangesets.
|
||||
*
|
||||
* Copyright (C) 2025 Cloud Software Group
|
||||
*/
|
||||
|
||||
#include "harness.h"
|
||||
|
||||
struct range {
|
||||
unsigned long start, end;
|
||||
};
|
||||
|
||||
struct action {
|
||||
enum {
|
||||
ADD,
|
||||
REMOVE,
|
||||
} action;
|
||||
struct range r;
|
||||
};
|
||||
|
||||
#define DECLARE_ACTIONS(nr) static const struct action actions ## nr []
|
||||
#define DECLARE_RESULTS(nr) static const struct range results ## nr []
|
||||
|
||||
/*
|
||||
* Subtract range with tail overlap on existing range:
|
||||
*
|
||||
* { 0, 1, 4, 5 } - { 3, 4 } = { 0, 1, 5, 5 }
|
||||
*/
|
||||
DECLARE_ACTIONS(0) = {
|
||||
{ ADD, { 0, 1 } },
|
||||
{ ADD, { 4, 5 } },
|
||||
{ REMOVE, { 3, 4 } },
|
||||
};
|
||||
DECLARE_RESULTS(0) = {
|
||||
{ 0, 1 }, { 5, 5 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Subtract range with complete and tail overlap on existing ranges:
|
||||
*
|
||||
* { 0, 1, 4, 5, 7, 8 } - { 3, 4, 5, 6, 7 } = { 0, 1, 8 }
|
||||
*/
|
||||
DECLARE_ACTIONS(1) = {
|
||||
{ ADD, { 0, 1 } },
|
||||
{ ADD, { 4, 5 } },
|
||||
{ ADD, { 7, 8 } },
|
||||
{ REMOVE, { 3, 7 } },
|
||||
};
|
||||
DECLARE_RESULTS(1) = {
|
||||
{ 0, 1 }, { 8, 8 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Subtract range with no overlap:
|
||||
*
|
||||
* { 0, 1, 4, 5 } - { 2, 3 } = { 0, 1, 4, 5 }
|
||||
*/
|
||||
DECLARE_ACTIONS(2) = {
|
||||
{ ADD, { 0, 1 } },
|
||||
{ ADD, { 4, 5 } },
|
||||
{ REMOVE, { 2, 3 } },
|
||||
};
|
||||
DECLARE_RESULTS(2) = {
|
||||
{ 0, 1 }, { 4, 5 },
|
||||
};
|
||||
|
||||
/*
|
||||
* Subtract range with partial overlap on two existing ranges:
|
||||
*
|
||||
* { 0, 1, 4, 5 } - { 1, 4 } = { 0, 5 }
|
||||
*/
|
||||
DECLARE_ACTIONS(3) = {
|
||||
{ ADD, { 0, 1 } },
|
||||
{ ADD, { 4, 5 } },
|
||||
{ REMOVE, { 1, 4 } },
|
||||
};
|
||||
DECLARE_RESULTS(3) = {
|
||||
{ 0, 0 }, { 5, 5 },
|
||||
};
|
||||
|
||||
static const struct test {
|
||||
unsigned int nr_actions, nr_results;
|
||||
const struct action *actions;
|
||||
const struct range *result;
|
||||
} tests[] = {
|
||||
#define DECLARE_TEST(nr) \
|
||||
{ \
|
||||
.actions = actions ## nr, \
|
||||
.nr_actions = ARRAY_SIZE(actions ## nr), \
|
||||
.result = results ## nr, \
|
||||
.nr_results = ARRAY_SIZE(results ## nr), \
|
||||
}
|
||||
|
||||
DECLARE_TEST(0),
|
||||
DECLARE_TEST(1),
|
||||
DECLARE_TEST(2),
|
||||
DECLARE_TEST(3),
|
||||
|
||||
#undef DECLARE_TEST
|
||||
};
|
||||
|
||||
static int print_range(unsigned long s, unsigned long e, void *data)
|
||||
{
|
||||
printf("[%ld, %ld]\n", s, e);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int count_ranges(unsigned long s, unsigned long e, void *data)
|
||||
{
|
||||
unsigned int *nr = data;
|
||||
|
||||
++*nr;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct range *expected;
|
||||
static int check_ranges(unsigned long s, unsigned long e, void *data)
|
||||
{
|
||||
unsigned int *nr = data;
|
||||
int rc = 0;
|
||||
|
||||
if ( s != expected[*nr].start || e != expected[*nr].end )
|
||||
rc = -EINVAL;
|
||||
|
||||
++*nr;
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void print_both(struct rangeset *r, const struct range *expected,
|
||||
unsigned int nr_expected)
|
||||
{
|
||||
unsigned int i;
|
||||
|
||||
printf("Result:\n");
|
||||
rangeset_report_ranges(r, 0, ~0UL, print_range, NULL);
|
||||
printf("Expected:\n");
|
||||
for ( i = 0; i < nr_expected; i++ )
|
||||
printf("[%ld, %ld]\n", expected[i].start, expected[i].end);
|
||||
}
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct rangeset *r = rangeset_new(NULL, NULL, 0);
|
||||
unsigned int i;
|
||||
int ret_code = 0;
|
||||
|
||||
ASSERT(r);
|
||||
|
||||
for ( i = 0 ; i < ARRAY_SIZE(tests); i++ )
|
||||
{
|
||||
unsigned int j, nr = 0;
|
||||
int rc = 0;
|
||||
|
||||
rangeset_purge(r);
|
||||
for ( j = 0; j < tests[i].nr_actions; j++ )
|
||||
{
|
||||
const struct action *a = &tests[i].actions[j];
|
||||
|
||||
switch ( a->action )
|
||||
{
|
||||
case ADD:
|
||||
rc = rangeset_add_range(r, a->r.start, a->r.end);
|
||||
break;
|
||||
|
||||
case REMOVE:
|
||||
rc = rangeset_remove_range(r, a->r.start, a->r.end);
|
||||
break;
|
||||
}
|
||||
|
||||
if ( rc )
|
||||
{
|
||||
printf("Test %u failed to %s range [%ld, %ld]\n",
|
||||
i, a->action == ADD ? "add" : "remove",
|
||||
a->r.start, a->r.end);
|
||||
rangeset_report_ranges(r, 0, ~0UL, print_range, NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( rc )
|
||||
{
|
||||
/* Action failed, skip this test and set exit code to failure. */
|
||||
ret_code = EXIT_FAILURE;
|
||||
continue;
|
||||
}
|
||||
|
||||
rc = rangeset_report_ranges(r, 0, ~0UL, count_ranges, &nr);
|
||||
if ( rc )
|
||||
{
|
||||
printf("Test %u unable to count number of result ranges\n", i);
|
||||
rangeset_report_ranges(r, 0, ~0UL, print_range, NULL);
|
||||
ret_code = EXIT_FAILURE;
|
||||
continue;
|
||||
}
|
||||
if ( nr != tests[i].nr_results )
|
||||
{
|
||||
printf("Test %u unexpected number of result ranges, expected: %u got: %u\n",
|
||||
i, tests[i].nr_results, nr);
|
||||
print_both(r, tests[i].result, tests[i].nr_results);
|
||||
ret_code = EXIT_FAILURE;
|
||||
continue;
|
||||
}
|
||||
|
||||
nr = 0;
|
||||
expected = tests[i].result;
|
||||
rc = rangeset_report_ranges(r, 0, ~0UL, check_ranges, &nr);
|
||||
if ( rc )
|
||||
{
|
||||
printf("Test %u range checking failed\n", i);
|
||||
print_both(r, tests[i].result, tests[i].nr_results);
|
||||
ret_code = EXIT_FAILURE;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Local variables:
|
||||
* mode: C
|
||||
* c-file-style: "BSD"
|
||||
* c-basic-offset: 4
|
||||
* indent-tabs-mode: nil
|
||||
* End:
|
||||
*/
|
||||