mirror of
https://github.com/luxfi/node.git
synced 2026-07-27 03:39:39 +00:00
101 lines
3.2 KiB
Go
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)
|
|
}
|
|
}
|
|
|