Files
node/vms/platformvm/platformvm_gpu_test.go
T

101 lines
3.2 KiB
Go

// Round-trip test for the platformvm GPU bridge.
//
// Validates the dlopen + dlsym path end-to-end:
//
// 1. AutoBackend() probes the canonical plugin order.
// 2. If a plugin is found, ValidatorSetApply is invoked with a 1-validator
// fixture (kVOpAdd) and must return without error.
// 3. The result must reflect the apply: applied_out == 1 AND the
// validator slot must be marked occupied + Active|PendingAdd.
//
// SKIP-on-miss is the canonical contract: if no plugin is on disk the
// test logs SKIP and exits 0. This is how the existing chains/aivm GPU
// tests behave — the bridge is optional and a missing plugin is not a
// failure mode of luxd itself.
//
// The launcher (lux_<backend>_platformvm_validator_set_apply) must NOT
// return any error code; any nonzero rc is a regression.
package platformvm
import (
"testing"
)
func TestPlatformVMGPUBridge_AutoBackend(t *testing.T) {
b := ActiveGPUBackend()
if b == nil || !b.IsAvailable() {
t.Skip("platformvm: no GPU plugin loaded — skipping bridge round-trip")
}
t.Logf("loaded GPU plugin: kind=%s path=%s", b.Kind(), b.Path())
// Single-validator fixture: ADD validator_id=42 with weight=1000.
desc := PVMRoundDescriptor{
ChainID: 0xCAFEBABE,
Round: 1,
Epoch: 10,
Mode: PVMModeValidator,
ValidatorOpCount: 1,
}
op := PVMValidatorOp{
ValidatorID: 42,
Weight: 1000,
Kind: PVMVOpAdd,
Epoch: 10,
}
// Power-of-two slot count — required by the open-addressing locator
// in platformvm_kernels_common.cuh (mask = count - 1).
validators := make([]PVMValidatorSlot, 8)
var applied uint32
err := b.ValidatorSetApply(&desc, []PVMValidatorOp{op}, validators, &applied)
if err != nil {
t.Fatalf("ValidatorSetApply: %v", err)
}
if applied != 1 {
t.Fatalf("ValidatorSetApply: applied=%d, want=1", applied)
}
// Locate the slot we wrote — open-addressing hash on validator_id.
found := false
for i := range validators {
if validators[i].Occupied != 0 && validators[i].ValidatorID == 42 {
found = true
if validators[i].Weight != 1000 {
t.Errorf("validator slot: Weight=%d, want=1000", validators[i].Weight)
}
wantStatus := PVMStatusActive | PVMStatusPendingAdd
if validators[i].Status != wantStatus {
t.Errorf("validator slot: Status=0x%x, want=0x%x",
validators[i].Status, wantStatus)
}
break
}
}
if !found {
t.Fatal("ValidatorSetApply: no occupied slot with validator_id=42")
}
}
// TestPlatformVMGPUBridge_NilHandle exercises the Zero-value GPUBackend
// behaviour. Every method must return ErrGPUNotAvailable when the handle
// is nil (the "no plugin loaded" case) without panicking. This is the
// fall-through contract the vm.go opt-in helpers rely on.
func TestPlatformVMGPUBridge_NilHandle(t *testing.T) {
var b *GPUBackend
if b.IsAvailable() {
t.Fatal("nil GPUBackend: IsAvailable() = true, want false")
}
if b.Kind() != GPUNone {
t.Fatalf("nil GPUBackend: Kind() = %s, want none", b.Kind())
}
if b.Path() != "" {
t.Fatalf("nil GPUBackend: Path() = %q, want empty", b.Path())
}
if err := b.Close(); err != nil {
t.Fatalf("nil GPUBackend: Close() = %v, want nil", err)
}
}