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