/* 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 #include #include #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; }