/* 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 ret_code; } /* * Local variables: * mode: C * c-file-style: "BSD" * c-basic-offset: 4 * indent-tabs-mode: nil * End: */