1 /* SPDX-License-Identifier: GPL-2.0-only */
2 /*
3  * Unit tests for rangesets.
4  *
5  * Copyright (C) 2025 Cloud Software Group
6  */
7 
8 #include "harness.h"
9 
10 struct range {
11     unsigned long start, end;
12 };
13 
14 struct action {
15     enum {
16         ADD,
17         REMOVE,
18     } action;
19     struct range r;
20 };
21 
22 #define DECLARE_ACTIONS(nr) static const struct action actions ## nr []
23 #define DECLARE_RESULTS(nr) static const struct range results ## nr []
24 
25 /*
26  * Subtract range with tail overlap on existing range:
27  *
28  * { 0, 1, 4, 5 } - { 3, 4 } = { 0, 1, 5, 5 }
29  */
30 DECLARE_ACTIONS(0) = {
31     { ADD,    { 0, 1 } },
32     { ADD,    { 4, 5 } },
33     { REMOVE, { 3, 4 } },
34 };
35 DECLARE_RESULTS(0) = {
36     { 0, 1 }, { 5, 5 },
37 };
38 
39 /*
40  * Subtract range with complete and tail overlap on existing ranges:
41  *
42  * { 0, 1, 4, 5, 7, 8 } - { 3, 4, 5, 6, 7 } = { 0, 1, 8 }
43  */
44 DECLARE_ACTIONS(1) = {
45     { ADD,    { 0, 1 } },
46     { ADD,    { 4, 5 } },
47     { ADD,    { 7, 8 } },
48     { REMOVE, { 3, 7 } },
49 };
50 DECLARE_RESULTS(1) = {
51     { 0, 1 }, { 8, 8 },
52 };
53 
54 /*
55  * Subtract range with no overlap:
56  *
57  * { 0, 1, 4, 5 } - { 2, 3 } = { 0, 1, 4, 5 }
58  */
59 DECLARE_ACTIONS(2) = {
60     { ADD,    { 0, 1 } },
61     { ADD,    { 4, 5 } },
62     { REMOVE, { 2, 3 } },
63 };
64 DECLARE_RESULTS(2) = {
65     { 0, 1 }, { 4, 5 },
66 };
67 
68 /*
69  * Subtract range with partial overlap on two existing ranges:
70  *
71  * { 0, 1, 4, 5 } - { 1, 4 } = { 0, 5 }
72  */
73 DECLARE_ACTIONS(3) = {
74     { ADD,    { 0, 1 } },
75     { ADD,    { 4, 5 } },
76     { REMOVE, { 1, 4 } },
77 };
78 DECLARE_RESULTS(3) = {
79     { 0, 0 }, { 5, 5 },
80 };
81 
82 static const struct test {
83     unsigned int nr_actions, nr_results;
84     const struct action *actions;
85     const struct range *result;
86 } tests[] = {
87 #define DECLARE_TEST(nr)                                \
88     {                                                   \
89         .actions = actions ## nr,                       \
90         .nr_actions = ARRAY_SIZE(actions ## nr),        \
91         .result  = results ## nr,                       \
92         .nr_results = ARRAY_SIZE(results ## nr),        \
93     }
94 
95     DECLARE_TEST(0),
96     DECLARE_TEST(1),
97     DECLARE_TEST(2),
98     DECLARE_TEST(3),
99 
100 #undef DECLARE_TEST
101 };
102 
print_range(unsigned long s,unsigned long e,void * data)103 static int print_range(unsigned long s, unsigned long e, void *data)
104 {
105     printf("[%ld, %ld]\n", s, e);
106 
107     return 0;
108 }
109 
count_ranges(unsigned long s,unsigned long e,void * data)110 static int count_ranges(unsigned long s, unsigned long e, void *data)
111 {
112     unsigned int *nr = data;
113 
114     ++*nr;
115     return 0;
116 }
117 
118 static const struct range *expected;
check_ranges(unsigned long s,unsigned long e,void * data)119 static int check_ranges(unsigned long s, unsigned long e, void *data)
120 {
121     unsigned int *nr = data;
122     int rc = 0;
123 
124     if ( s != expected[*nr].start || e != expected[*nr].end )
125         rc = -EINVAL;
126 
127     ++*nr;
128     return rc;
129 }
130 
print_both(struct rangeset * r,const struct range * expected,unsigned int nr_expected)131 static void print_both(struct rangeset *r, const struct range *expected,
132                        unsigned int nr_expected)
133 {
134     unsigned int i;
135 
136     printf("Result:\n");
137     rangeset_report_ranges(r, 0, ~0UL, print_range, NULL);
138     printf("Expected:\n");
139     for ( i = 0; i < nr_expected; i++ )
140         printf("[%ld, %ld]\n", expected[i].start, expected[i].end);
141 }
142 
main(int argc,char ** argv)143 int main(int argc, char **argv)
144 {
145     struct rangeset *r = rangeset_new(NULL, NULL, 0);
146     unsigned int i;
147     int ret_code = 0;
148 
149     ASSERT(r);
150 
151     for ( i = 0 ; i < ARRAY_SIZE(tests); i++ )
152     {
153         unsigned int j, nr = 0;
154         int rc = 0;
155 
156         rangeset_purge(r);
157         for ( j = 0; j < tests[i].nr_actions; j++ )
158         {
159             const struct action *a = &tests[i].actions[j];
160 
161             switch ( a->action )
162             {
163             case ADD:
164                 rc = rangeset_add_range(r, a->r.start, a->r.end);
165                 break;
166 
167             case REMOVE:
168                 rc = rangeset_remove_range(r, a->r.start, a->r.end);
169                 break;
170             }
171 
172             if ( rc )
173             {
174                 printf("Test %u failed to %s range [%ld, %ld]\n",
175                        i, a->action == ADD ? "add" : "remove",
176                        a->r.start, a->r.end);
177                 rangeset_report_ranges(r, 0, ~0UL, print_range, NULL);
178                 break;
179             }
180         }
181 
182         if ( rc )
183         {
184             /* Action failed, skip this test and set exit code to failure. */
185             ret_code = EXIT_FAILURE;
186             continue;
187         }
188 
189         rc = rangeset_report_ranges(r, 0, ~0UL, count_ranges, &nr);
190         if ( rc )
191         {
192             printf("Test %u unable to count number of result ranges\n", i);
193             rangeset_report_ranges(r, 0, ~0UL, print_range, NULL);
194             ret_code = EXIT_FAILURE;
195             continue;
196         }
197         if ( nr != tests[i].nr_results )
198         {
199             printf("Test %u unexpected number of result ranges, expected: %u got: %u\n",
200                    i, tests[i].nr_results, nr);
201             print_both(r, tests[i].result, tests[i].nr_results);
202             ret_code = EXIT_FAILURE;
203             continue;
204         }
205 
206         nr = 0;
207         expected = tests[i].result;
208         rc = rangeset_report_ranges(r, 0, ~0UL, check_ranges, &nr);
209         if ( rc )
210         {
211             printf("Test %u range checking failed\n", i);
212             print_both(r, tests[i].result, tests[i].nr_results);
213             ret_code = EXIT_FAILURE;
214             continue;
215         }
216     }
217 
218     return ret_code;
219 }
220 
221 /*
222  * Local variables:
223  * mode: C
224  * c-file-style: "BSD"
225  * c-basic-offset: 4
226  * indent-tabs-mode: nil
227  * End:
228  */
229