test-mergesort: add test subcommand
Adapt the qsort certification program from "Engineering a Sort Function" by Bentley and McIlroy for testing our linked list sort function. It generates several lists with various distribution patterns and counts the number of operations llist_mergesort() needs to order them. It compares the result to the output of a trusted sort function (qsort(1)) and also checks if the sort is stable. Also add a test script that makes use of the new subcommand. Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>
René Scharfe committed
Oct 1, 2021 at 11:12 UTC
e031e9719d21c11102870b8037bdda995dede5e0
2 files changed
+242
-1
t/helper/test-mergesort.c
+231
-1
@@ -50,9 +50,239 @@ static int sort_stdin(void)
50
return 0;
51
}
52
53
+static void dist_sawtooth(int *arr, int n, int m)
54
+{
55
+ int i;
56
+ for (i = 0; i < n; i++)
57
+ arr[i] = i % m;
58
+}
59
+
60
+static void dist_rand(int *arr, int n, int m)
61
+{
62
+ int i;
63
+ for (i = 0; i < n; i++)
64
+ arr[i] = rand() % m;
65
+}
66
+
67
+static void dist_stagger(int *arr, int n, int m)
68
+{
69
+ int i;
70
+ for (i = 0; i < n; i++)
71
+ arr[i] = (i * m + i) % n;
72
+}
73
+
74
+static void dist_plateau(int *arr, int n, int m)
75
+{
76
+ int i;
77
+ for (i = 0; i < n; i++)
78
+ arr[i] = (i < m) ? i : m;
79
+}
80
+
81
+static void dist_shuffle(int *arr, int n, int m)
82
+{
83
+ int i, j, k;
84
+ for (i = j = 0, k = 1; i < n; i++)
85
+ arr[i] = (rand() % m) ? (j += 2) : (k += 2);
86
+}
87
+
88
+#define DIST(name) { #name, dist_##name }
89
+
90
+static struct dist {
91
+ const char *name;
92
+ void (*fn)(int *arr, int n, int m);
93
+} dist[] = {
94
+ DIST(sawtooth),
95
+ DIST(rand),
96
+ DIST(stagger),
97
+ DIST(plateau),
98
+ DIST(shuffle),
99
+};
100
+
101
+static void mode_copy(int *arr, int n)
102
+{
103
+ /* nothing */
104
+}
105
+
106
+static void mode_reverse(int *arr, int n)
107
+{
108
+ int i, j;
109
+ for (i = 0, j = n - 1; i < j; i++, j--)
110
+ SWAP(arr[i], arr[j]);
111
+}
112
+
113
+static void mode_reverse_1st_half(int *arr, int n)
114
+{
115
+ mode_reverse(arr, n / 2);
116
+}
117
+
118
+static void mode_reverse_2nd_half(int *arr, int n)
119
+{
120
+ int half = n / 2;
121
+ mode_reverse(arr + half, n - half);
122
+}
123
+
124
+static int compare_ints(const void *av, const void *bv)
125
+{
126
+ const int *ap = av, *bp = bv;
127
+ int a = *ap, b = *bp;
128
+ return (a > b) - (a < b);
129
+}
130
+
131
+static void mode_sort(int *arr, int n)
132
+{
133
+ QSORT(arr, n, compare_ints);
134
+}
135
+
136
+static void mode_dither(int *arr, int n)
137
+{
138
+ int i;
139
+ for (i = 0; i < n; i++)
140
+ arr[i] += i % 5;
141
+}
142
+
143
+#define MODE(name) { #name, mode_##name }
144
+
145
+static struct mode {
146
+ const char *name;
147
+ void (*fn)(int *arr, int n);
148
+} mode[] = {
149
+ MODE(copy),
150
+ MODE(reverse),
151
+ MODE(reverse_1st_half),
152
+ MODE(reverse_2nd_half),
153
+ MODE(sort),
154
+ MODE(dither),
155
+};
156
+
157
+static struct stats {
158
+ int get_next, set_next, compare;
159
+} stats;
160
+
161
+struct number {
162
+ int value, rank;
163
+ struct number *next;
164
+};
165
+
166
+static void *get_next_number(const void *a)
167
+{
168
+ stats.get_next++;
169
+ return ((const struct number *)a)->next;
170
+}
171
+
172
+static void set_next_number(void *a, void *b)
173
+{
174
+ stats.set_next++;
175
+ ((struct number *)a)->next = b;
176
+}
177
+
178
+static int compare_numbers(const void *av, const void *bv)
179
+{
180
+ const struct number *an = av, *bn = bv;
181
+ int a = an->value, b = bn->value;
182
+ stats.compare++;
183
+ return (a > b) - (a < b);
184
+}
185
+
186
+static void clear_numbers(struct number *list)
187
+{
188
+ while (list) {
189
+ struct number *next = list->next;
190
+ free(list);
191
+ list = next;
192
+ }
193
+}
194
+
195
+static int test(const struct dist *dist, const struct mode *mode, int n, int m)
196
+{
197
+ int *arr;
198
+ size_t i;
199
+ struct number *curr, *list, **tail;
200
+ int is_sorted = 1;
201
+ int is_stable = 1;
202
+ const char *verdict;
203
+ int result = -1;
204
+
205
+ ALLOC_ARRAY(arr, n);
206
+ dist->fn(arr, n, m);
207
+ mode->fn(arr, n);
208
+ for (i = 0, tail = &list; i < n; i++) {
209
+ curr = xmalloc(sizeof(*curr));
210
+ curr->value = arr[i];
211
+ curr->rank = i;
212
+ *tail = curr;
213
+ tail = &curr->next;
214
+ }
215
+ *tail = NULL;
216
+
217
+ stats.get_next = stats.set_next = stats.compare = 0;
218
+ list = llist_mergesort(list, get_next_number, set_next_number,
219
+ compare_numbers);
220
+
221
+ QSORT(arr, n, compare_ints);
222
+ for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) {
223
+ if (arr[i] != curr->value)
224
+ is_sorted = 0;
225
+ if (curr->next && curr->value == curr->next->value &&
226
+ curr->rank >= curr->next->rank)
227
+ is_stable = 0;
228
+ }
229
+ if (i < n) {
230
+ verdict = "too short";
231
+ } else if (curr) {
232
+ verdict = "too long";
233
+ } else if (!is_sorted) {
234
+ verdict = "not sorted";
235
+ } else if (!is_stable) {
236
+ verdict = "unstable";
237
+ } else {
238
+ verdict = "OK";
239
+ result = 0;
240
+ }
241
+
242
+ printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n",
243
+ dist->name, mode->name, n, m, stats.get_next, stats.set_next,
244
+ stats.compare, verdict);
245
+
246
+ clear_numbers(list);
247
+ free(arr);
248
+
249
+ return result;
250
+}
251
+
252
+/*
253
+ * A version of the qsort certification program from "Engineering a Sort
254
+ * Function" by Bentley and McIlroy, Software—Practice and Experience,
255
+ * Volume 23, Issue 11, 1249–1265 (November 1993).
256
+ */
257
+static int run_tests(int argc, const char **argv)
258
+{
259
+ const char *argv_default[] = { "100", "1023", "1024", "1025" };
260
+ if (!argc)
261
+ return run_tests(ARRAY_SIZE(argv_default), argv_default);
262
+ printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n",
263
+ "distribut", "mode", "n", "m", "get_next", "set_next",
264
+ "compare", "verdict");
265
+ while (argc--) {
266
+ int i, j, m, n = strtol(*argv++, NULL, 10);
267
+ for (i = 0; i < ARRAY_SIZE(dist); i++) {
268
+ for (j = 0; j < ARRAY_SIZE(mode); j++) {
269
+ for (m = 1; m < 2 * n; m *= 2) {
270
+ if (test(&dist[i], &mode[j], n, m))
271
+ return 1;
272
+ }
273
+ }
274
+ }
275
+ }
276
+ return 0;
277
+}
278
+
279
int cmd__mergesort(int argc, const char **argv)
280
{
281
if (argc == 2 && !strcmp(argv[1], "sort"))
282
return sort_stdin();
57
- usage("test-tool mergesort sort");
283
+ if (argc > 1 && !strcmp(argv[1], "test"))
284
+ return run_tests(argc - 2, argv + 2);
285
+ fprintf(stderr, "usage: test-tool mergesort sort\n");
286
+ fprintf(stderr, " or: test-tool mergesort test [<n>...]\n");
287
+ return 129;
288
}
t/t0071-sort.sh
new
+11
@@ -0,0 +1,11 @@
1
+#!/bin/sh
2
+
3
+test_description='verify sort functions'
4
+
5
+. ./test-lib.sh
6
+
7
+test_expect_success 'llist_mergesort()' '
8
+ test-tool mergesort test
9
+'
10
+
11
+test_done