2
0
Эх сурвалжийг харах

testqsort: use SDLTest_TestSuite

Anonymous Maarten 1 сар өмнө
parent
commit
2be18f340f
2 өөрчлөгдсөн 465 нэмэгдсэн , 67 устгасан
  1. 3 0
      test/CMakeLists.txt
  2. 462 67
      test/testqsort.c

+ 3 - 0
test/CMakeLists.txt

@@ -420,6 +420,9 @@ add_sdl_test_executable(testrendercopyex NEEDS_RESOURCES TESTUTILS SOURCES testr
 add_sdl_test_executable(testmessage SOURCES testmessage.c)
 add_sdl_test_executable(testdisplayinfo SOURCES testdisplayinfo.c)
 add_sdl_test_executable(testqsort NONINTERACTIVE SOURCES testqsort.c)
+if(EMSCRIPTEN)
+    target_link_options(testqsort PRIVATE -sALLOW_MEMORY_GROWTH)
+endif()
 add_sdl_test_executable(testbounds NONINTERACTIVE SOURCES testbounds.c)
 add_sdl_test_executable(testcustomcursor SOURCES testcustomcursor.c)
 add_sdl_test_executable(testvulkan NO_C90 SOURCES testvulkan.c)

+ 462 - 67
test/testqsort.c

@@ -14,10 +14,20 @@
 #include <SDL3/SDL_main.h>
 #include <SDL3/SDL_test.h>
 
+typedef struct {
+    Uint8 major;
+    Uint8 minor;
+    Uint8 micro;
+} VersionTuple;
+
 static int a_global_var = 77;
+static int (SDLCALL * global_compare_cbfn)(const void *_a, const void *_b);
+
+static unsigned long arraylens[16] = { 12, 1024 * 100 };
+static unsigned int count_arraylens = 2;
 
 static int SDLCALL
-num_compare(const void *_a, const void *_b)
+compare_int(const void *_a, const void *_b)
 {
     const int a = *((const int *)_a);
     const int b = *((const int *)_b);
@@ -25,51 +35,429 @@ num_compare(const void *_a, const void *_b)
 }
 
 static int SDLCALL
-num_compare_r(void *userdata, const void *a, const void *b)
+compare_float(const void *_a, const void *_b)
+{
+    const float a = *((const float *)_a);
+    const float b = *((const float *)_b);
+    return (a < b) ? -1 : ((a > b) ? 1 : 0);
+}
+
+static int SDLCALL
+compare_double(const void *_a, const void *_b)
+{
+    const double a = *((const double *)_a);
+    const double b = *((const double *)_b);
+    return (a < b) ? -1 : ((a > b) ? 1 : 0);
+}
+
+static int SDLCALL
+compare_intptr(const void *_a, const void *_b)
+{
+    const int* a = *((const int **)_a);
+    const int* b = *((const int **)_b);
+    return compare_int(a, b);
+}
+
+static int SDLCALL
+compare_version(const void *_a, const void *_b)
+{
+    const VersionTuple *a = ((const VersionTuple *)_a);
+    const VersionTuple *b = ((const VersionTuple *)_b);
+    int d;
+    d = (int)a->major - (int)b->major;
+    if (d != 0) {
+        return d;
+    }
+    d = (int)a->minor - (int)b->minor;
+    if (d != 0) {
+        return d;
+    }
+    return (int)a->micro - (int)b->micro;
+}
+
+static int SDLCALL
+generic_compare_r(void *userdata, const void *a, const void *b)
 {
     if (userdata != &a_global_var) {
-        SDL_Log("Uhoh, invalid userdata during qsort!");
+        SDLTest_AssertCheck(userdata == &a_global_var, "User data of callback must be identical to global data");
     }
-    return num_compare(a, b);
+    return global_compare_cbfn(a, b);
 }
 
-static void
-test_sort(const char *desc, int *nums, const int arraylen)
+#define STR2(S) "[" #S "] "
+#define STR(S) STR2(S)
+
+#define TEST_ARRAY_IS_SORTED(TYPE, ARRAY, SIZE, IS_LE)                                                    \
+    do {                                                                                                  \
+        size_t sorted_index;                                                                              \
+        Uint64 count_non_sorted = 0;                                                                      \
+        for (sorted_index = 0; sorted_index < (SIZE) - 1; sorted_index++) {                               \
+            if (!IS_LE((ARRAY)[sorted_index], (ARRAY)[sorted_index + 1])) {                               \
+                count_non_sorted += 1;                                                                    \
+            }                                                                                             \
+        }                                                                                                 \
+        SDLTest_AssertCheck(count_non_sorted == 0,                                                        \
+            STR(TYPE) "Array (size=%d) is sorted (bad count=%" SDL_PRIu64 ")", (SIZE), count_non_sorted); \
+    } while (0)
+
+/* This test is O(n^2), so very slow (a hashmap can speed this up):
+ * - we cannot trust qsort
+ * - the arrays can contain duplicate items
+ * - the arrays are not int
+ */
+#define TEST_ARRAY_LOST_NO_ELEMENTS(TYPE, ORIGINAL, SORTED, SIZE, IS_SAME)                       \
+    do {                                                                                         \
+        size_t original_index;                                                                   \
+        bool *original_seen = SDL_calloc((SIZE), sizeof(bool));                                  \
+        Uint64 lost_count = 0;                                                                   \
+        SDL_assert(original_seen != NULL);                                                       \
+        for (original_index = 0; original_index < (SIZE); original_index++) {                    \
+            size_t sorted_index;                                                                 \
+            for (sorted_index = 0; sorted_index < (SIZE); sorted_index++) {                      \
+                if (IS_SAME((ORIGINAL)[original_index], (SORTED)[sorted_index])) {               \
+                    original_seen[original_index] = true;                                        \
+                    break;                                                                       \
+                }                                                                                \
+            }                                                                                    \
+        }                                                                                        \
+        for (original_index = 0; original_index < (SIZE); original_index++) {                    \
+            if (!original_seen[original_index]) {                                                \
+                SDLTest_AssertCheck(original_seen[original_index],                               \
+                    STR(TYPE) "Element %d is missing in the sorted array", (int)original_index); \
+                lost_count += 1;                                                                 \
+            }                                                                                    \
+        }                                                                                        \
+        SDLTest_AssertCheck(lost_count == 0,                                                     \
+            STR(TYPE) "No elements were lost (lost count=%" SDL_PRIu64 ")", lost_count);         \
+        SDL_free(original_seen);                                                                 \
+    } while (0)
+
+#define TEST_QSORT_ARRAY_GENERIC(TYPE, ARRAY, SIZE, QSORT_CALL, CHECK_ARRAY_ELEMS, IS_LE, WHAT) \
+    do {                                                                                        \
+        SDL_memcpy(sorted, (ARRAY), sizeof(TYPE) * (SIZE));                                     \
+        SDLTest_AssertPass(STR(TYPE) "About to call " WHAT "(%d, %d)",                          \
+            (int)(SIZE), (int)sizeof(TYPE));                                                    \
+        QSORT_CALL;                                                                             \
+        SDLTest_AssertPass(STR(TYPE) WHAT " finished");                                         \
+        TEST_ARRAY_IS_SORTED(TYPE, sorted, SIZE, IS_LE);                                        \
+        SDLTest_AssertPass(STR(TYPE) "Verifying element preservation...");                      \
+        CHECK_ARRAY_ELEMS(TYPE, sorted, (ARRAY), (SIZE));                                       \
+    } while (0)
+
+#define TEST_QSORT_ARRAY_QSORT(TYPE, ARRAY, SIZE, COMPARE_CBFN, CHECK_ARRAY_ELEMS, IS_LE)          \
+    do {                                                                                           \
+        SDLTest_AssertPass(STR(TYPE) "Testing SDL_qsort of array with size %u", (unsigned)(SIZE)); \
+        global_compare_cbfn = NULL;                                                                \
+        TEST_QSORT_ARRAY_GENERIC(TYPE, ARRAY, SIZE,                                                \
+            SDL_qsort(sorted, (SIZE), sizeof(TYPE), COMPARE_CBFN),                                 \
+            CHECK_ARRAY_ELEMS, IS_LE, "SDL_qsort");                                                \
+    } while (0);
+
+#define TEST_QSORT_ARRAY_QSORT_R(TYPE, ARRAY, SIZE, COMPARE_CBFN, CHECK_ARRAY_ELEMS, IS_LE)          \
+    do {                                                                                             \
+        SDLTest_AssertPass(STR(TYPE) "Testing SDL_qsort_r of array with size %u", (unsigned)(SIZE)); \
+        global_compare_cbfn = (COMPARE_CBFN);                                                        \
+        TEST_QSORT_ARRAY_GENERIC(TYPE, ARRAY, SIZE,                                                  \
+            SDL_qsort_r(sorted, (SIZE), sizeof(TYPE), generic_compare_r, &a_global_var),             \
+            CHECK_ARRAY_ELEMS, IS_LE, "SDL_qsort_r");                                                \
+    } while (0);
+
+#define TEST_QSORT_ARRAY(TYPE, ARRAY, SIZE, COMPARE_CBFN, CHECK_ARRAY_ELEMS, IS_LE)          \
+    do {                                                                                     \
+        TYPE *sorted = SDL_calloc((SIZE), sizeof(TYPE));                                     \
+        SDL_assert(sorted != NULL);                                                          \
+                                                                                             \
+        TEST_QSORT_ARRAY_QSORT(TYPE, ARRAY, SIZE, COMPARE_CBFN, CHECK_ARRAY_ELEMS, IS_LE);   \
+                                                                                             \
+        TEST_QSORT_ARRAY_QSORT_R(TYPE, ARRAY, SIZE, COMPARE_CBFN, CHECK_ARRAY_ELEMS, IS_LE); \
+                                                                                             \
+        SDL_free(sorted);                                                                    \
+    } while (0)
+
+#define INT_ISLE(A, B) ((A) <= (B))
+
+#define INTPTR_ISLE(A, B) (*(A) <= *(B))
+
+#define FLOAT_ISLE(A, B) ((A) <= (B))
+
+#define DOUBLE_ISLE(A, B) ((A) <= (B))
+
+#define VERSION_ISLE(A, B) (compare_version(&(A), &(B)) <= 0)
+
+#define CHECK_ELEMS_SORTED_ARRAY(TYPE, SORTED, INPUT, SIZE)          \
+    do {                                                             \
+        unsigned int check_index;                                    \
+        for (check_index = 0; check_index < (SIZE); check_index++) { \
+            if ((SORTED)[check_index] != (INPUT)[check_index]) {     \
+              SDLTest_AssertCheck(false,                             \
+                "sorted[%u] == input[%u]",                           \
+                check_index, check_index);                           \
+            }                                                        \
+        }                                                            \
+    } while (0)
+
+static int SDLCALL qsort_testAlreadySorted(void *arg)
 {
-    static int nums_copy[1024 * 100];
-    int i;
-    int prev;
+    unsigned int iteration;
+    (void)arg;
 
-    SDL_assert(SDL_arraysize(nums_copy) >= arraylen);
+    for (iteration = 0; iteration < count_arraylens; iteration++) {
+        const unsigned int arraylen = arraylens[iteration];
+        unsigned int i;
+        int *ints = SDL_malloc(sizeof(int) * arraylen);
+        int **intptrs = SDL_malloc(sizeof(int *) * arraylen);
+        double *doubles = SDL_malloc(sizeof(double) * arraylen);
 
-    SDL_Log("test: %s arraylen=%d", desc, arraylen);
+        for (i = 0; i < arraylen; i++) {
+            ints[i] = i;
+            intptrs[i] = &ints[i];
+            doubles[i] = (double)i * SDL_PI_D;
+        }
+        TEST_QSORT_ARRAY(int, ints, arraylen, compare_int, CHECK_ELEMS_SORTED_ARRAY, INT_ISLE);
+        TEST_QSORT_ARRAY(int *, intptrs, arraylen, compare_intptr, CHECK_ELEMS_SORTED_ARRAY, INTPTR_ISLE);
+        TEST_QSORT_ARRAY(double, doubles, arraylen, compare_double, CHECK_ELEMS_SORTED_ARRAY, DOUBLE_ISLE);
+
+        SDL_free(ints);
+        SDL_free(intptrs);
+        SDL_free(doubles);
+    }
+    return TEST_COMPLETED;
+}
+
+#define CHECK_ELEMS_SORTED_ARRAY_EXCEPT_LAST(TYPE, SORTED, INPUT, SIZE)                             \
+    do {                                                                                            \
+        unsigned int check_index;                                                                   \
+        for (check_index = 0; check_index < (SIZE) - 1; check_index++) {                            \
+            if (SDL_memcmp(&(SORTED)[check_index + 1], &(INPUT)[check_index], sizeof(TYPE)) != 0) { \
+              SDLTest_AssertCheck(false,                                                            \
+                STR(TYPE) "sorted[%u] == input[%u]",                                                \
+                check_index + 1, check_index);                                                      \
+            }                                                                                       \
+        }                                                                                           \
+    } while (0)
 
-    SDL_memcpy(nums_copy, nums, arraylen * sizeof (*nums));
+static int SDLCALL qsort_testAlreadySortedExceptLast(void *arg)
+{
+    unsigned int iteration;
+    (void)arg;
 
-    SDL_qsort(nums, arraylen, sizeof(nums[0]), num_compare);
-    SDL_qsort_r(nums_copy, arraylen, sizeof(nums[0]), num_compare_r, &a_global_var);
+    for (iteration = 0; iteration < count_arraylens; iteration++) {
+        const unsigned int arraylen = arraylens[iteration];
+        unsigned int i;
+        int *ints = SDL_malloc(sizeof(int) * arraylen);
+        int **intptrs = SDL_malloc(sizeof(int *) * arraylen);
+        double *doubles = SDL_malloc(sizeof(double) * arraylen);
+        VersionTuple *versions = SDL_calloc(arraylen, sizeof(VersionTuple));
 
-    prev = nums[0];
-    for (i = 1; i < arraylen; i++) {
-        const int val = nums[i];
-        const int val2 = nums_copy[i];
-        if ((val < prev) || (val != val2)) {
-            SDL_Log("sort is broken!");
-            return;
+        for (i = 0; i < arraylen; i++) {
+            ints[i] = i;
+            intptrs[i] = &ints[i];
+            doubles[i] = (double)i * SDL_PI_D;
+            versions[i].micro = (i + 1) % 256;
+            versions[i].minor = (i + 1) % (256 * 256) / 256;
+            versions[i].major = (i + 1) % (256 * 256 * 256) / 256 / 256;
         }
-        prev = val;
+        ints[arraylen - 1] = -1;
+        doubles[arraylen - 1] = -1.;
+        versions[arraylen - 1].major = 0;
+        versions[arraylen - 1].minor = 0;
+        versions[arraylen - 1].micro = 0;
+        TEST_QSORT_ARRAY(int, ints, arraylen, compare_int, CHECK_ELEMS_SORTED_ARRAY_EXCEPT_LAST, INT_ISLE);
+        TEST_QSORT_ARRAY(int *, intptrs, arraylen, compare_intptr, CHECK_ELEMS_SORTED_ARRAY_EXCEPT_LAST, INTPTR_ISLE);
+        TEST_QSORT_ARRAY(double, doubles, arraylen, compare_double, CHECK_ELEMS_SORTED_ARRAY_EXCEPT_LAST, DOUBLE_ISLE);
+        TEST_QSORT_ARRAY(VersionTuple, versions, arraylen, compare_version, CHECK_ELEMS_SORTED_ARRAY_EXCEPT_LAST, VERSION_ISLE);
+
+        SDL_free(ints);
+        SDL_free(intptrs);
+        SDL_free(doubles);
+        SDL_free(versions);
     }
+    return TEST_COMPLETED;
 }
 
+#define CHECK_ELEMS_SORTED_ARRAY_REVERSED(TYPE, SORTED, INPUT, SIZE)                                         \
+    do {                                                                                                     \
+        unsigned int check_index;                                                                            \
+        for (check_index = 0; check_index < (SIZE); check_index++) {                                         \
+            if (SDL_memcmp(&(SORTED)[check_index], &(INPUT)[(SIZE) - check_index - 1], sizeof(TYPE)) != 0) { \
+              SDLTest_AssertCheck(false,                                                                     \
+                STR(TYPE) "sorted[%u] != input[%u]",                                                         \
+                check_index, (SIZE) - check_index - 1);                                                      \
+            }                                                                                                \
+        }                                                                                                    \
+    } while (0)
+
+static int SDLCALL qsort_testReverseSorted(void *arg)
+{
+    unsigned int iteration;
+    (void)arg;
+
+    for (iteration = 0; iteration < count_arraylens; iteration++) {
+        const unsigned int arraylen = arraylens[iteration];
+        unsigned int i;
+        int *ints = SDL_malloc(sizeof(int) * arraylen);
+        int **intptrs = SDL_malloc(sizeof(int *) * arraylen);
+        double *doubles = SDL_malloc(sizeof(double) * arraylen);
+        VersionTuple *versions = SDL_calloc(arraylen, sizeof(VersionTuple));
+
+        for (i = 0; i < arraylen; i++) {
+            ints[i] = (arraylen - 1) - i;
+            intptrs[i] = &ints[i];
+            doubles[i] = (double)((arraylen - 1) - i) * SDL_PI_D;
+            versions[i].micro = ints[i] % 256;
+            versions[i].minor = ints[i] % (256 * 256) / 256;
+            versions[i].major = ints[i] % (256 * 256 * 256) / 256 / 256;
+        }
+        TEST_QSORT_ARRAY(int, ints, arraylen, compare_int, CHECK_ELEMS_SORTED_ARRAY_REVERSED, INT_ISLE);
+        TEST_QSORT_ARRAY(int *, intptrs, arraylen, compare_intptr, CHECK_ELEMS_SORTED_ARRAY_REVERSED, INTPTR_ISLE);
+        TEST_QSORT_ARRAY(double, doubles, arraylen, compare_double, CHECK_ELEMS_SORTED_ARRAY_REVERSED, DOUBLE_ISLE);
+        TEST_QSORT_ARRAY(VersionTuple, versions, arraylen, compare_version, CHECK_ELEMS_SORTED_ARRAY_REVERSED, VERSION_ISLE);
+
+        SDL_free(ints);
+        SDL_free(intptrs);
+        SDL_free(doubles);
+        SDL_free(versions);
+    }
+    return TEST_COMPLETED;
+}
+
+#define MAX_RANDOM_INT_VALUE (1024 * 1024)
+
+#define CHECK_ELEMS_SORTED_ARRAY_RANDOM_INT(TYPE, SORTED, INPUT, SIZE)             \
+    do {                                                                           \
+        int *presences = SDL_calloc(MAX_RANDOM_INT_VALUE, sizeof(int));            \
+        unsigned int check_index;                                                  \
+        for (check_index = 0; check_index < (SIZE); check_index++) {               \
+            presences[(SORTED)[check_index]] += 1;                                 \
+            presences[(INPUT)[check_index]] -= 1;                                  \
+        }                                                                          \
+        for (check_index = 0; check_index < MAX_RANDOM_INT_VALUE; check_index++) { \
+            if (presences[check_index] != 0) {                                     \
+                SDLTest_AssertCheck(false, "Value %d appears %s in sorted array",  \
+                    check_index,                                                   \
+                    presences[check_index] > 0 ? "MORE" : "LESS");                 \
+            }                                                                      \
+        }                                                                          \
+        SDL_free(presences);                                                       \
+    } while (0)
+
+#define VERSION_TO_INT(VERSION) (((VERSION).major * 256 + (VERSION).minor) * 256 + (VERSION).micro)
+#define INT_VERSION_MAJOR(V)       (((V) / (256 * 256) % 256))
+#define INT_VERSION_MINOR(V)       (((V) / (256)) % 256)
+#define INT_VERSION_MICRO(V)       ((V) % 256)
+
+#define CHECK_ELEMS_SORTED_ARRAY_RANDOM_VERSION(TYPE, SORTED, INPUT, SIZE)                          \
+    do {                                                                                            \
+        int *presences = SDL_calloc(256 * 256 * 256, sizeof(int));                                  \
+        unsigned int check_index;                                                                   \
+        for (check_index = 0; check_index < (SIZE); check_index++) {                                \
+            presences[VERSION_TO_INT((SORTED)[check_index])] += 1;                                  \
+            presences[VERSION_TO_INT((INPUT)[check_index])] -= 1;                                   \
+        }                                                                                           \
+        for (check_index = 0; check_index < 256 * 256 * 256; check_index++) {                       \
+            if (presences[check_index] != 0) {                                                      \
+                SDLTest_AssertCheck(false, STR(TYPE) "Version %d.%d.%d appears %s in sorted array", \
+                    INT_VERSION_MAJOR(check_index),                                                 \
+                    INT_VERSION_MINOR(check_index),                                                 \
+                    INT_VERSION_MICRO(check_index),                                                 \
+                    presences[check_index] > 0 ? "MORE" : "LESS");                                  \
+            }                                                                                       \
+        }                                                                                           \
+        SDL_free(presences);                                                                        \
+    } while (0)
+
+#define CHECK_ELEMS_SORTED_ARRAY_RPS(TYPE, SORTED, INPUT, SIZE)                        \
+    do {                                                                               \
+        int presences[3] = { 0 };                                                      \
+        unsigned int check_index;                                                      \
+        for (check_index = 0; check_index < (SIZE); check_index++) {                   \
+            presences[(SORTED)[check_index]] += 1;                                     \
+            presences[(INPUT)[check_index]] -= 1;                                      \
+        }                                                                              \
+        for (check_index = 0; check_index < SDL_arraysize(presences); check_index++) { \
+            if (presences[check_index] != 0) {                                         \
+                SDLTest_AssertCheck(false, STR(TYPE) "%d appeared or disappeared",     \
+                    check_index);                                                      \
+            }                                                                          \
+        }                                                                              \
+    } while (0)
+
+#define CHECK_ELEMS_SORTED_ARRAY_RANDOM_NOP(TYPE, SORTED, INPUT, SIZE) \
+    SDLTest_AssertPass(STR(TYPE) "Skipping elements presence check")
+
+static int SDLCALL qsort_testRandomSorted(void *arg)
+{
+    unsigned int iteration;
+    (void)arg;
+
+    for (iteration = 0; iteration < count_arraylens; iteration++) {
+        const unsigned int arraylen = arraylens[iteration];
+        unsigned int i;
+        int *ints = SDL_malloc(sizeof(int) * arraylen);
+        float *floats = SDL_malloc(sizeof(float) * arraylen);
+        VersionTuple *versions = SDL_calloc(arraylen, sizeof(VersionTuple));
+
+        for (i = 0; i < arraylen; i++) {
+            ints[i] = SDLTest_RandomIntegerInRange(0, MAX_RANDOM_INT_VALUE - 1);
+            floats[i] = SDLTest_RandomFloat() * SDL_PI_F;
+            versions[i].micro = SDLTest_RandomIntegerInRange(0, 255);
+            versions[i].minor = SDLTest_RandomIntegerInRange(0, 255);
+            versions[i].major = SDLTest_RandomIntegerInRange(0, 255);
+        }
+        TEST_QSORT_ARRAY(int, ints, arraylen, compare_int, CHECK_ELEMS_SORTED_ARRAY_RANDOM_INT, INT_ISLE);
+        TEST_QSORT_ARRAY(float, floats, arraylen, compare_float, CHECK_ELEMS_SORTED_ARRAY_RANDOM_NOP, FLOAT_ISLE);
+        TEST_QSORT_ARRAY(VersionTuple, versions, arraylen, compare_version, CHECK_ELEMS_SORTED_ARRAY_RANDOM_VERSION, VERSION_ISLE);
+
+        SDL_free(ints);
+        SDL_free(floats);
+        SDL_free(versions);
+    }
+    return TEST_COMPLETED;
+}
+
+static const SDLTest_TestCaseReference qsortTestAlreadySorted = {
+    qsort_testAlreadySorted, "qsort_testAlreadySorted", "Test sorting already sorted array", TEST_ENABLED
+};
+
+static const SDLTest_TestCaseReference qsortTestAlreadySortedExceptLast = {
+    qsort_testAlreadySortedExceptLast, "qsort_testAlreadySortedExceptLast", "Test sorting nearly sorted array (last item is not in order)", TEST_ENABLED
+};
+
+static const SDLTest_TestCaseReference qsortTestReverseSorted = {
+    qsort_testReverseSorted, "qsort_testReverseSorted", "Test sorting an array in reverse order", TEST_ENABLED
+};
+
+static const SDLTest_TestCaseReference qsortTestRandomSorted = {
+    qsort_testRandomSorted, "qsort_testRandomSorted", "Test sorting a random array", TEST_ENABLED
+};
+
+static const SDLTest_TestCaseReference *qsortTests[] = {
+    &qsortTestAlreadySorted,
+    &qsortTestAlreadySortedExceptLast,
+    &qsortTestReverseSorted,
+    &qsortTestRandomSorted,
+    NULL
+};
+
+static SDLTest_TestSuiteReference qsortTestSuite = {
+    "qsort",
+    NULL,
+    qsortTests,
+    NULL
+};
+
+static SDLTest_TestSuiteReference *testSuites[] = {
+    &qsortTestSuite,
+    NULL
+};
+
 int main(int argc, char *argv[])
 {
-    static int nums[1024 * 100];
-    static const int itervals[] = { SDL_arraysize(nums), 12 };
     int i;
-    int iteration;
+    int result;
     SDLTest_CommonState *state;
-    Uint64 seed = 0;
-    int seed_seen = 0;
+    SDLTest_TestSuiteRunner *runner;
+    bool list = false;
 
     /* Initialize test framework */
     state = SDLTest_CommonCreateState(argv, 0);
@@ -77,27 +465,47 @@ int main(int argc, char *argv[])
         return 1;
     }
 
+    runner = SDLTest_CreateTestSuiteRunner(state, testSuites);
+
     /* Parse commandline */
     for (i = 1; i < argc;) {
         int consumed;
 
         consumed = SDLTest_CommonArg(state, i);
         if (!consumed) {
-            if (!seed_seen) {
-                char *endptr = NULL;
-
-                seed = (Uint64)SDL_strtoull(argv[i], &endptr, 0);
-                if (endptr != argv[i] && *endptr == '\0') {
-                    seed_seen = 1;
-                    consumed = 1;
-                } else {
-                    SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Invalid seed. Use a decimal or hexadecimal number.");
-                    return 1;
+
+            if (SDL_strcasecmp(argv[i], "--array-lengths") == 0) {
+                count_arraylens = 0;
+                consumed = 1;
+                while (argv[i + consumed] && argv[i + consumed][0] != '-') {
+                    char *endptr = NULL;
+                    unsigned int arraylen = (unsigned int)SDL_strtoul(argv[i + 1], &endptr, 10);
+                    if (*endptr != '\0') {
+                        count_arraylens = 0;
+                        break;
+                    }
+                    if (count_arraylens >= SDL_arraysize(arraylens)) {
+                        SDL_LogWarn(SDL_LOG_CATEGORY_TEST, "Dropping array length %d", arraylen);
+                    } else {
+                        arraylens[count_arraylens] = arraylen;
+                    }
+                    count_arraylens++;
+                    consumed++;
+                }
+                if (consumed == 0) {
+                    SDL_LogError(SDL_LOG_CATEGORY_TEST, "--array-lengths needs positive int numbers");
                 }
+            } else if (SDL_strcasecmp(argv[i], "--list") == 0) {
+                consumed = 1;
+                list = true;
             }
         }
         if (consumed <= 0) {
-            static const char *options[] = { "[seed]", NULL };
+            static const char *options[] = {
+                "[--list]",
+                "[--array-lengths N1 [N2 [N3 [...]]]",
+                NULL
+            };
             SDLTest_CommonLogUsage(state, argv[0], options);
             return 1;
         }
@@ -105,38 +513,25 @@ int main(int argc, char *argv[])
         i += consumed;
     }
 
-    if (!seed_seen) {
-        seed = SDL_GetPerformanceCounter();
-    }
-    SDL_Log("Using random seed 0x%" SDL_PRIx64, seed);
-
-    for (iteration = 0; iteration < SDL_arraysize(itervals); iteration++) {
-        const int arraylen = itervals[iteration];
-
-        for (i = 0; i < arraylen; i++) {
-            nums[i] = i;
-        }
-        test_sort("already sorted", nums, arraylen);
-
-        for (i = 0; i < arraylen; i++) {
-            nums[i] = i;
-        }
-        nums[arraylen - 1] = -1;
-        test_sort("already sorted except last element", nums, arraylen);
-
-        for (i = 0; i < arraylen; i++) {
-            nums[i] = (arraylen - 1) - i;
-        }
-        test_sort("reverse sorted", nums, arraylen);
-
-        for (i = 0; i < arraylen; i++) {
-            nums[i] = SDL_rand_r(&seed, 1000000);
+    /* List all suites. */
+    if (list) {
+        int suiteCounter;
+        for (suiteCounter = 0; testSuites[suiteCounter]; ++suiteCounter) {
+            int testCounter;
+            SDLTest_TestSuiteReference *testSuite = testSuites[suiteCounter];
+            SDL_Log("Test suite: %s", testSuite->name);
+            for (testCounter = 0; testSuite->testCases[testCounter]; ++testCounter) {
+                const SDLTest_TestCaseReference *testCase = testSuite->testCases[testCounter];
+                SDL_Log("      test: %s%s", testCase->name, testCase->enabled ? "" : " (disabled)");
+            }
         }
-        test_sort("random sorted", nums, arraylen);
+        result = 0;
+    } else {
+        result = SDLTest_ExecuteTestSuiteRunner(runner);
     }
 
     SDL_Quit();
+    SDLTest_DestroyTestSuiteRunner(runner);
     SDLTest_CommonDestroyState(state);
-
-    return 0;
+    return result;
 }