Files
gpu/a_setup_test.go
zeekay 17c3bd541d test: use LUXCPP_DIR env var instead of hardcoded /Users/z paths
Matches the convention in ~/work/lux/CLAUDE.md — luxcpp source root
resolves via LUXCPP_DIR, defaulting to $HOME/work/luxcpp.
2026-06-01 13:57:44 -07:00

92 lines
3.0 KiB
Go

//go:build cgo
// Copyright (C) 2025-2026, Lux Industries Inc. All rights reserved.
// See the file LICENSE for licensing terms.
package gpu
import (
"os"
"testing"
)
// Pin the backend plugin search path + Metal kernel root so the vtbl-loaded
// backends pick up the freshly-built artifacts in ~/work/luxcpp/gpu/build and
// the metal shader sources alongside them, rather than whichever older copy
// happens to live under /usr/local/lib.
//
// PluginLoader resolves LUX_GPU_BACKEND_PATH at C++ static-initializer time
// (the first lux_gpu_create() triggers construction of the singleton). Go's
// package init() also calls lux_gpu_create(). To race ahead of either, the
// env vars MUST be set before the cgo runtime initializes — which we achieve
// by setting them in this package's own init() block, ordered to run before
// the gpu_cgo.go init() via Go's lexical filename ordering (this file is
// named a_setup_test.go so it sorts first among test files; non-test files
// have their inits run in lex order alongside).
//
// Note: this file does NOT use cgo (cgo in *_test.go is unsupported). It
// uses pure Go os.Setenv, which on Darwin/Linux propagates through the libc
// environ via runtime_setenv, so the C++ initializers of libluxgpu observe
// the values on first call.
// luxcppDir returns the luxcpp source root. LUXCPP_DIR overrides the default
// of $HOME/work/luxcpp.
func luxcppDir() string {
if d := os.Getenv("LUXCPP_DIR"); d != "" {
return d
}
return os.ExpandEnv("$HOME/work/luxcpp")
}
func defaultLuxBackendPath() string {
return luxcppDir() + "/gpu/build/metal_backend"
}
func defaultLuxMetalKernels() string {
return luxcppDir() + "/metal/src/shaders"
}
// applyBackendEnv pushes the backend env vars into the process environment
// if they are unset (idempotent on test re-runs).
func applyBackendEnv() {
backendPath := defaultLuxBackendPath()
if existing := os.Getenv("LUX_GPU_BACKEND_PATH"); existing == "" {
_ = os.Setenv("LUX_GPU_BACKEND_PATH", backendPath)
} else if !containsPath(existing, backendPath) {
_ = os.Setenv("LUX_GPU_BACKEND_PATH",
backendPath+":"+existing)
}
if os.Getenv("LUX_METAL_KERNEL_PATH") == "" {
_ = os.Setenv("LUX_METAL_KERNEL_PATH", defaultLuxMetalKernels())
}
}
// containsPath returns true if elem appears as a colon-separated segment of
// list. Used to keep applyBackendEnv idempotent across t.Setenv reentrancy.
func containsPath(list, elem string) bool {
start := 0
for i := 0; i <= len(list); i++ {
if i == len(list) || list[i] == ':' {
if list[start:i] == elem {
return true
}
start = i + 1
}
}
return false
}
func init() {
// init() in a _test.go file is part of the test binary's init graph and
// runs alongside non-test inits. Lexical file order ensures this runs
// before gpu_cgo.go's init() under the standard Go toolchain.
applyBackendEnv()
}
// TestMain re-applies the env (so subprocess-style runners that clear the
// process environ between tests still see our values) and runs the suite.
func TestMain(m *testing.M) {
applyBackendEnv()
os.Exit(m.Run())
}