File size: 6,427 Bytes
8d0b310
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
/* tests/test_gpu_args.c — unit tests for the --gpu-vram /
 * --gpu-devices parser. Compiles against the CPU/non-CUDA build of
 * ds4_gpu_args.c (so the "auto" branch reports the build-mode error
 * rather than calling cuda). The CUDA path is exercised on box smoke
 * runs.
 */
#include "../ds4_gpu_args.h"
#include "../ds4_gpu_mgpu.h"

#include <stdbool.h>
#include <stdio.h>
#include <string.h>

#define PASS_FAIL(name, cond)                                                \
    do {                                                                     \
        if (cond) {                                                          \
            fprintf(stdout, "ok %s\n", name);                                \
        } else {                                                             \
            fprintf(stdout, "FAIL %s\n", name);                              \
            failed++;                                                        \
        }                                                                    \
    } while (0)

int main(void) {
    int failed = 0;
    char err[256];

    /* 1: explicit 2-device split */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        err[0] = '\0';
        int rc = parse_gpu_vram_arg("40,12", NULL, &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 40,12 returns 0",
                  rc == 0 && !skip && cfg.n_gpus == 2 &&
                  cfg.device_indices[0] == 0 && cfg.device_indices[1] == 1 &&
                  cfg.vram_bytes[0] == (size_t)40 * 1024 * 1024 * 1024 &&
                  cfg.vram_bytes[1] == (size_t)12 * 1024 * 1024 * 1024);
    }

    /* 2: single-GPU */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("40", NULL, &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 40 → 1 device",
                  rc == 0 && !skip && cfg.n_gpus == 1 &&
                  cfg.device_indices[0] == 0 &&
                  cfg.vram_bytes[0] == (size_t)40 * 1024 * 1024 * 1024);
    }

    /* 3: --gpu-vram 0 → skip-cuda */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("0", NULL, &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 0 → skip_cuda=true", rc == 0 && skip);
    }

    /* 4: "auto" without CUDA build → error */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("auto", NULL, &cfg, &skip, err, sizeof(err));
#if !defined(DS4_NO_GPU) && !defined(__APPLE__)
        /* CUDA build: linker resolves ds4_gpu_args_probe_auto_cuda. We
         * don't run that here (no CUDA hardware in unit test), so we
         * only assert the function returned with something reasonable.
         * This test is mostly CPU/Metal-focused. */
        PASS_FAIL("parse auto (cuda build) doesn't NULL-deref",
                  (rc == 0 || rc != 0));  /* tautological — auto path
                                              exists; box smoke covers it */
#else
        PASS_FAIL("parse auto rejects on CPU/Metal build",
                  rc != 0 && strstr(err, "auto") != NULL &&
                  strstr(err, "CUDA build") != NULL);
#endif
    }

    /* 5: count mismatch */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("40,12,40", "0,2", &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 40,12,40 with devs 0,2 errors on count",
                  rc != 0 && strstr(err, "count") != NULL);
    }

    /* 6: garbage value */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("abc", NULL, &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse abc errors", rc != 0);
    }

    /* 7: explicit devices with explicit vram */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("40,12", "0,2", &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 40,12 + devices 0,2",
                  rc == 0 && !skip && cfg.n_gpus == 2 &&
                  cfg.device_indices[0] == 0 && cfg.device_indices[1] == 2 &&
                  cfg.vram_bytes[0] == (size_t)40 * 1024 * 1024 * 1024 &&
                  cfg.vram_bytes[1] == (size_t)12 * 1024 * 1024 * 1024);
    }

    /* 8: --gpu-vram 0 with --gpu-devices → error */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("0", "0,1", &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 0 + devices errors", rc != 0);
    }

    /* 9: trailing comma is a parse error */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("40,", NULL, &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse 40, (trailing comma) errors", rc != 0);
    }

    /* 10: huge value rejected (overflow guard) */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("9999999", NULL, &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse huge vram rejected (overflow guard)", rc != 0);
    }

    /* 11: huge device index rejected */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        bool skip = false;
        int rc = parse_gpu_vram_arg("40,12", "0,99999",
                                     &cfg, &skip, err, sizeof(err));
        PASS_FAIL("parse huge device index rejected", rc != 0);
    }

    /* 12: format_gpu_layout_line */
    {
        ds4_gpu_config cfg = (ds4_gpu_config){0};
        cfg.n_gpus = 2;
        cfg.device_indices[0] = 0;
        cfg.device_indices[1] = 1;
        cfg.vram_bytes[0] = (size_t)40 * 1024 * 1024 * 1024;
        cfg.vram_bytes[1] = (size_t)12 * 1024 * 1024 * 1024;
        char out[256];
        int n = format_gpu_layout_line(&cfg, true, out, sizeof(out));
        PASS_FAIL("format_gpu_layout_line", n > 0 &&
                  strstr(out, "2 devices [0,1]") != NULL &&
                  strstr(out, "budgets 40,12 GB") != NULL &&
                  strstr(out, "auto=true") != NULL);
    }

    if (failed > 0) {
        fprintf(stderr, "test_gpu_args: %d test(s) FAILED\n", failed);
        return 1;
    }
    fprintf(stdout, "test_gpu_args: all tests passed\n");
    return 0;
}