Skip to content

Commit 3422807

Browse files
committed
Refactor testqsort to test non-aligned, aligned, and non-transitive sorting.
1 parent cad2dd8 commit 3422807

File tree

1 file changed

+239
-24
lines changed

1 file changed

+239
-24
lines changed

test/testqsort.c

Lines changed: 239 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,20 @@
1414
#include <SDL3/SDL_main.h>
1515
#include <SDL3/SDL_test.h>
1616

17+
/* Required to test non-aligned sort */
18+
typedef char non_word_value[sizeof(int) * 2];
19+
20+
typedef struct
21+
{
22+
size_t len;
23+
size_t item_size;
24+
void *aligned;
25+
void *unaligned;
26+
} test_buffer;
27+
1728
static int a_global_var = 77;
29+
static int qsort_is_broken;
30+
static SDLTest_RandomContext rndctx;
1831

1932
static int SDLCALL
2033
num_compare(const void *_a, const void *_b)
@@ -24,54 +37,240 @@ num_compare(const void *_a, const void *_b)
2437
return (a < b) ? -1 : ((a > b) ? 1 : 0);
2538
}
2639

40+
static int SDLCALL
41+
num_compare_non_transitive(const void *_a, const void *_b)
42+
{
43+
const int a = *((const int *)_a);
44+
const int b = *((const int *)_b);
45+
return (a < b) ? 0 : 1;
46+
}
47+
2748
static int SDLCALL
2849
num_compare_r(void *userdata, const void *a, const void *b)
2950
{
3051
if (userdata != &a_global_var) {
3152
SDL_Log("Uhoh, invalid userdata during qsort!");
53+
qsort_is_broken = 1;
3254
}
3355
return num_compare(a, b);
3456
}
3557

58+
/* Will never return 0 */
59+
static int SDLCALL
60+
num_compare_random_any(const void *a, const void *b)
61+
{
62+
(void)(a);
63+
(void)(b);
64+
return (SDLTest_RandomInt(&rndctx) > ((Uint32)SDL_MAX_SINT32)) ? 1 : -1;
65+
}
66+
67+
static int SDLCALL
68+
num_compare_non_word(const void *_a, const void *_b)
69+
{
70+
const int a = (int)(((const char *)_a)[0]);
71+
const int b = (int)(((const char *)_b)[0]);
72+
return num_compare(&a, &b);
73+
}
74+
75+
static int SDLCALL
76+
num_compare_non_word_r(void *userdata, const void *_a, const void *_b)
77+
{
78+
const int a = (int)(((const char *)_a)[0]);
79+
const int b = (int)(((const char *)_b)[0]);
80+
return num_compare_r(userdata, &a, &b);
81+
}
82+
83+
static int SDLCALL
84+
num_compare_non_word_non_transitive(const void *_a, const void *_b)
85+
{
86+
const int a = (int)(((char *)_a)[0]);
87+
const int b = (int)(((char *)_b)[0]);
88+
return num_compare_non_transitive(&a, &b);
89+
}
90+
3691
static void
37-
test_sort(const char *desc, int *nums, const int arraylen)
92+
alloc_buffer(test_buffer *buffer, size_t size, size_t len)
3893
{
39-
static int nums_copy[1024 * 100];
40-
int i;
41-
int prev;
94+
uintptr_t base;
4295

43-
SDL_assert(SDL_arraysize(nums_copy) >= arraylen);
96+
SDL_memset(buffer, 0, sizeof(*buffer));
97+
buffer->len = len;
98+
buffer->item_size = size;
4499

45-
SDL_Log("test: %s arraylen=%d", desc, arraylen);
100+
if (len == 0) {
101+
return;
102+
}
46103

47-
SDL_memcpy(nums_copy, nums, arraylen * sizeof (*nums));
104+
buffer->aligned = SDL_malloc((len + 1) * size + sizeof(char));
105+
base = (uintptr_t)(buffer->aligned);
106+
/* Should be aligned */
107+
SDL_assert(((base | size) & (sizeof(int) - 1)) == 0);
48108

49-
SDL_qsort(nums, arraylen, sizeof(nums[0]), num_compare);
50-
SDL_qsort_r(nums_copy, arraylen, sizeof(nums[0]), num_compare_r, &a_global_var);
109+
buffer->unaligned = (void *)(base + sizeof(char));
110+
}
51111

52-
prev = nums[0];
53-
for (i = 1; i < arraylen; i++) {
112+
static void
113+
check_sort(const int *nums, const int *r_nums, size_t numlen) {
114+
int i;
115+
int prev = 0;
116+
117+
if (numlen > 0) {
118+
prev = nums[0];
119+
}
120+
for (i = 1; i < numlen; i++) {
54121
const int val = nums[i];
55-
const int val2 = nums_copy[i];
122+
const int val2 = r_nums[i];
56123
if ((val < prev) || (val != val2)) {
57124
SDL_Log("sort is broken!");
58-
return;
125+
qsort_is_broken = 1;
126+
break;
59127
}
60128
prev = val;
61129
}
62130
}
63131

64-
int main(int argc, char *argv[])
132+
static void
133+
test_sort(const char *desc, const int *nums, size_t numlen)
134+
{
135+
test_buffer buffer;
136+
test_buffer buffer_r;
137+
138+
alloc_buffer(&buffer, sizeof(nums[0]), numlen);
139+
alloc_buffer(&buffer_r, sizeof(nums[0]), numlen);
140+
141+
SDL_Log("test: %s bufferlen=%d", desc, (int)(buffer.len));
142+
143+
/* Test aligned sort */
144+
if (buffer.aligned != NULL) {
145+
/* memcpy with NULL is UB */
146+
SDL_memcpy(buffer.aligned, nums, numlen * sizeof(nums[0]));
147+
SDL_memcpy(buffer_r.aligned, nums, numlen * sizeof(nums[0]));
148+
}
149+
SDL_qsort(buffer.aligned, numlen, sizeof(nums[0]), num_compare);
150+
SDL_qsort_r(buffer_r.aligned, numlen, sizeof(nums[0]), num_compare_r, &a_global_var);
151+
check_sort(buffer.aligned, buffer_r.aligned, numlen);
152+
153+
/* We can't test unaligned int sort because it's UB. */
154+
155+
SDL_free(buffer_r.aligned);
156+
SDL_free(buffer.aligned);
157+
}
158+
159+
static void
160+
check_non_word_sort(const non_word_value *nums, const non_word_value *r_nums, size_t numlen) {
161+
int i;
162+
char prev;
163+
164+
if (numlen > 0) {
165+
prev = nums[0][0];
166+
}
167+
for (i = 1; i < numlen; i++) {
168+
const char val = nums[i][0];
169+
const char val2 = r_nums[i][0];
170+
if ((val < prev) || (val != val2)) {
171+
SDL_Log("sort is broken!");
172+
qsort_is_broken = 1;
173+
break;
174+
}
175+
prev = val;
176+
}
177+
}
178+
179+
static void
180+
test_sort_non_word(const char *desc, const non_word_value *nums, size_t numlen)
65181
{
66-
static int nums[1024 * 100];
67-
static const int itervals[] = { SDL_arraysize(nums), 12 };
182+
test_buffer buffer;
183+
test_buffer buffer_r;
184+
185+
alloc_buffer(&buffer, sizeof(nums[0]), numlen);
186+
alloc_buffer(&buffer_r, sizeof(nums[0]), numlen);
187+
188+
SDL_Log("test: %s non-word numlen=%d", desc, (int)(numlen));
189+
190+
/* Test aligned sort */
191+
if (buffer.aligned != NULL) {
192+
SDL_memcpy(buffer.aligned, nums, numlen * sizeof(nums[0]));
193+
SDL_memcpy(buffer_r.aligned, nums, numlen * sizeof(nums[0]));
194+
}
195+
SDL_qsort(buffer.aligned, numlen, sizeof(nums[0]), num_compare_non_word);
196+
SDL_qsort_r(buffer_r.aligned, numlen, sizeof(nums[0]), num_compare_non_word_r, &a_global_var);
197+
check_non_word_sort(buffer.aligned, buffer_r.aligned, numlen);
198+
199+
/* Test non-aligned sort */
200+
if (buffer.unaligned != NULL) {
201+
SDL_memcpy(buffer.unaligned, nums, numlen * sizeof(nums[0]));
202+
SDL_memcpy(buffer_r.unaligned, nums, numlen * sizeof(nums[0]));
203+
}
204+
SDL_qsort(buffer.unaligned, numlen, sizeof(nums[0]), num_compare_non_word);
205+
SDL_qsort_r(buffer_r.unaligned, numlen, sizeof(nums[0]), num_compare_non_word_r, &a_global_var);
206+
check_non_word_sort(buffer.unaligned, buffer_r.unaligned, numlen);
207+
208+
SDL_free(buffer_r.aligned);
209+
SDL_free(buffer.aligned);
210+
}
211+
212+
static void
213+
test_sort_non_transitive(size_t numlen) {
68214
int i;
69-
int iteration;
70-
SDLTest_RandomContext rndctx;
71-
SDLTest_CommonState *state;
215+
test_buffer buffer;
216+
test_buffer non_word_buffer;
217+
int *nums;
218+
non_word_value *non_word_nums;
219+
220+
SDL_Log("test: non-transitive numlen=%d", (int)(numlen));
221+
222+
alloc_buffer(&buffer, sizeof(nums[0]), numlen);
223+
alloc_buffer(&non_word_buffer, sizeof(non_word_nums[0]), numlen);
224+
225+
/* Test aligned sort */
226+
nums = buffer.aligned;
227+
non_word_nums = non_word_buffer.aligned;
228+
for (i = 0; i < numlen; i++) {
229+
nums[i] = (int)(numlen) - i;
230+
non_word_nums[i][0] = (char)nums[i];
231+
}
232+
233+
SDL_qsort(nums, numlen, sizeof(nums[0]), num_compare_non_transitive);
234+
SDL_qsort(non_word_nums, numlen, sizeof(non_word_nums[0]), num_compare_non_word_non_transitive);
235+
236+
/* What's inside doesn't matter for random comparison */
237+
SDL_qsort(nums, numlen, sizeof(nums[0]), num_compare_random_any);
238+
SDL_qsort(non_word_nums, numlen, sizeof(non_word_nums[0]), num_compare_random_any);
239+
240+
/* Test non-aligned sort */
241+
non_word_nums = non_word_buffer.unaligned;
242+
for (i = 0; i < numlen; i++) {
243+
non_word_nums[i][0] = (char)((int)(numlen) - i);
244+
}
245+
246+
SDL_qsort(non_word_nums, numlen, sizeof(non_word_nums[0]), num_compare_non_word_non_transitive);
247+
SDL_qsort(non_word_nums, numlen, sizeof(non_word_nums[0]), num_compare_random_any);
248+
249+
SDL_free(buffer.aligned);
250+
SDL_free(non_word_buffer.aligned);
251+
}
252+
253+
int main(int argc, char *argv[])
254+
{
255+
static int nums[1024 * 128];
256+
static non_word_value non_word_nums[1024 * 128];
257+
258+
SDL_assert(SDL_arraysize(nums) == SDL_arraysize(non_word_nums));
259+
260+
/* Test truncation points */
261+
static const int trunc = 12;
262+
static const int trunc_words = 12 * sizeof(int);
263+
static const int itervals[] = { 0, trunc, 15, trunc_words, SDL_arraysize(nums) };
264+
/* Non-transitive sorting consumes much more CPU, use smaller value */
265+
static const int itervals_non_transitive[] = { 0, trunc, 15, trunc_words, 16384};
266+
267+
int i = 0;
268+
int iteration = 0;
269+
SDLTest_CommonState *state = NULL;
72270
int seed_seen = 0;
73271

74272
SDL_zero(rndctx);
273+
qsort_is_broken = 0;
75274

76275
/* Initialize test framework */
77276
state = SDLTest_CommonCreateState(argv, 0);
@@ -123,27 +322,43 @@ int main(int argc, char *argv[])
123322

124323
for (i = 0; i < arraylen; i++) {
125324
nums[i] = i;
325+
non_word_nums[i][0] = (char)i;
126326
}
127327
test_sort("already sorted", nums, arraylen);
328+
test_sort_non_word("already_sorted", non_word_nums, arraylen);
128329

129-
for (i = 0; i < arraylen; i++) {
130-
nums[i] = i;
330+
if (arraylen > 0) {
331+
for (i = 0; i < arraylen; i++) {
332+
nums[i] = i;
333+
non_word_nums[i][0] = (char)i;
334+
}
335+
nums[arraylen - 1] = -1;
336+
test_sort("already sorted except last element", nums, arraylen);
337+
test_sort_non_word("already sorted except last element", non_word_nums, arraylen);
131338
}
132-
nums[arraylen - 1] = -1;
133-
test_sort("already sorted except last element", nums, arraylen);
134339

135340
for (i = 0; i < arraylen; i++) {
136341
nums[i] = (arraylen - 1) - i;
342+
non_word_nums[i][0] = (char)i;
137343
}
138344
test_sort("reverse sorted", nums, arraylen);
345+
test_sort_non_word("reverse sorted", non_word_nums, arraylen);
139346

140347
for (i = 0; i < arraylen; i++) {
141348
nums[i] = SDLTest_RandomInt(&rndctx);
349+
non_word_nums[i][0] = (char)i;
142350
}
143351
test_sort("random sorted", nums, arraylen);
352+
test_sort_non_word("random sorted", non_word_nums, arraylen);
353+
}
354+
355+
for (iteration = 0; iteration < SDL_arraysize(itervals_non_transitive); iteration++) {
356+
const int arraylen = itervals_non_transitive[iteration];
357+
358+
test_sort_non_transitive(arraylen);
144359
}
145360

146361
SDLTest_CommonDestroyState(state);
147362

148-
return 0;
363+
return qsort_is_broken;
149364
}

0 commit comments

Comments
 (0)