mirror of
https://github.com/luxfi/consensus.git
synced 2026-09-24 00:26:58 +00:00
245 lines
6.5 KiB
Go
245 lines
6.5 KiB
Go
// Copyright (C) 2019-2025, Lux Industries Inc. All rights reserved.
|
|
// See the file LICENSE for licensing terms.
|
|
|
|
package ai
|
|
|
|
import (
|
|
"testing"
|
|
)
|
|
|
|
// TestMLXBackendInitialization tests MLX backend initialization
|
|
func TestMLXBackendInitialization(t *testing.T) {
|
|
backend, err := NewMLXBackend(100)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize MLX backend: %v", err)
|
|
}
|
|
|
|
// When running without accel, the backend may not be enabled
|
|
// This is expected behavior - skip tests that require acceleration
|
|
if !backend.IsEnabled() {
|
|
t.Skip("MLX backend not available (requires accel build tag)")
|
|
}
|
|
|
|
if backend.GetDeviceInfo() == "" {
|
|
t.Error("Device info should not be empty")
|
|
}
|
|
|
|
if backend.batchSize != 100 {
|
|
t.Errorf("Expected batch size 100, got %d", backend.batchSize)
|
|
}
|
|
}
|
|
|
|
// TestMLXBackendProcessVotes tests vote processing
|
|
func TestMLXBackendProcessVotes(t *testing.T) {
|
|
backend, err := NewMLXBackend(10)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize MLX backend: %v", err)
|
|
}
|
|
|
|
// Create test votes
|
|
votes := make([]Vote, 10)
|
|
for i := range votes {
|
|
votes[i] = Vote{
|
|
VoterID: [32]byte{byte(i), byte(i + 1)},
|
|
BlockID: [32]byte{byte(i * 2), byte(i*2 + 1)},
|
|
IsPreference: i%2 == 0,
|
|
}
|
|
}
|
|
|
|
// Process votes
|
|
processed, err := backend.ProcessVotesBatch(votes)
|
|
if err != nil {
|
|
t.Fatalf("Failed to process votes: %v", err)
|
|
}
|
|
|
|
if processed != len(votes) {
|
|
t.Errorf("Expected to process %d votes, got %d", len(votes), processed)
|
|
}
|
|
|
|
// Check throughput - only expect positive throughput when enabled
|
|
throughput := backend.GetThroughput()
|
|
if backend.IsEnabled() && throughput <= 0 {
|
|
t.Error("Throughput should be positive after processing when enabled")
|
|
}
|
|
t.Logf("Processing throughput: %.2f votes/sec", throughput)
|
|
}
|
|
|
|
// TestMLXBackendEmptyBatch tests processing empty batch
|
|
func TestMLXBackendEmptyBatch(t *testing.T) {
|
|
backend, err := NewMLXBackend(10)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize MLX backend: %v", err)
|
|
}
|
|
|
|
// Process empty batch
|
|
processed, err := backend.ProcessVotesBatch([]Vote{})
|
|
if err != nil {
|
|
t.Fatalf("Failed to process empty batch: %v", err)
|
|
}
|
|
|
|
if processed != 0 {
|
|
t.Errorf("Expected to process 0 votes, got %d", processed)
|
|
}
|
|
}
|
|
|
|
// TestMLXBackendLargeBatch tests processing large batches
|
|
func TestMLXBackendLargeBatch(t *testing.T) {
|
|
backend, err := NewMLXBackend(10000)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize MLX backend: %v", err)
|
|
}
|
|
|
|
// Create large batch of votes
|
|
votes := make([]Vote, 10000)
|
|
for i := range votes {
|
|
votes[i] = Vote{
|
|
VoterID: [32]byte{byte(i), byte(i >> 8), byte(i >> 16)},
|
|
BlockID: [32]byte{byte(i * 2), byte((i * 2) >> 8), byte((i * 2) >> 16)},
|
|
IsPreference: i%2 == 0,
|
|
}
|
|
}
|
|
|
|
// Process votes
|
|
processed, err := backend.ProcessVotesBatch(votes)
|
|
if err != nil {
|
|
t.Fatalf("Failed to process large batch: %v", err)
|
|
}
|
|
|
|
if processed != len(votes) {
|
|
t.Errorf("Expected to process %d votes, got %d", len(votes), processed)
|
|
}
|
|
|
|
throughput := backend.GetThroughput()
|
|
t.Logf("Large batch throughput: %.2f votes/sec", throughput)
|
|
}
|
|
|
|
// TestMLXBackendConcurrency tests concurrent vote processing
|
|
func TestMLXBackendConcurrency(t *testing.T) {
|
|
backend, err := NewMLXBackend(100)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize MLX backend: %v", err)
|
|
}
|
|
|
|
// Create votes for concurrent processing
|
|
votes := make([]Vote, 100)
|
|
for i := range votes {
|
|
votes[i] = Vote{
|
|
VoterID: [32]byte{byte(i)},
|
|
BlockID: [32]byte{byte(i * 2)},
|
|
IsPreference: i%2 == 0,
|
|
}
|
|
}
|
|
|
|
// Run concurrent processing
|
|
done := make(chan bool, 3)
|
|
for i := 0; i < 3; i++ {
|
|
go func() {
|
|
defer func() { done <- true }()
|
|
_, err := backend.ProcessVotesBatch(votes)
|
|
if err != nil {
|
|
t.Errorf("Concurrent processing failed: %v", err)
|
|
}
|
|
}()
|
|
}
|
|
|
|
// Wait for all goroutines
|
|
for i := 0; i < 3; i++ {
|
|
<-done
|
|
}
|
|
|
|
// Check throughput after concurrent processing - only expect positive when enabled
|
|
throughput := backend.GetThroughput()
|
|
if backend.IsEnabled() && throughput <= 0 {
|
|
t.Error("Throughput should be positive after concurrent processing when enabled")
|
|
}
|
|
t.Logf("Concurrent processing throughput: %.2f votes/sec", throughput)
|
|
}
|
|
|
|
// TestAccelBackendInitialization tests direct AccelBackend usage
|
|
func TestAccelBackendInitialization(t *testing.T) {
|
|
backend, err := NewAccelBackend(100)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize AccelBackend: %v", err)
|
|
}
|
|
|
|
// When running without accel, the backend may not be enabled
|
|
// This is expected behavior - skip tests that require acceleration
|
|
if !backend.IsEnabled() {
|
|
t.Skip("AccelBackend not available (requires accel build tag)")
|
|
}
|
|
|
|
if backend.GetDeviceInfo() == "" {
|
|
t.Error("Device info should not be empty")
|
|
}
|
|
}
|
|
|
|
// TestAccelBackendProcessVotes tests AccelBackend vote processing
|
|
func TestAccelBackendProcessVotes(t *testing.T) {
|
|
backend, err := NewAccelBackend(10)
|
|
if err != nil {
|
|
t.Fatalf("Failed to initialize AccelBackend: %v", err)
|
|
}
|
|
|
|
votes := make([]Vote, 10)
|
|
for i := range votes {
|
|
votes[i] = Vote{
|
|
VoterID: [32]byte{byte(i)},
|
|
BlockID: [32]byte{byte(i * 2)},
|
|
IsPreference: i%2 == 0,
|
|
}
|
|
}
|
|
|
|
processed, err := backend.ProcessVotesBatch(votes)
|
|
if err != nil {
|
|
t.Fatalf("Failed to process votes: %v", err)
|
|
}
|
|
|
|
if processed != len(votes) {
|
|
t.Errorf("Expected to process %d votes, got %d", len(votes), processed)
|
|
}
|
|
}
|
|
|
|
// TestBackendFallbackFunctionality tests that the pure-Go fallback works correctly.
|
|
func TestBackendFallbackFunctionality(t *testing.T) {
|
|
backend, err := NewBackend(100)
|
|
if err != nil {
|
|
t.Fatalf("Failed to create backend: %v", err)
|
|
}
|
|
|
|
// Test that ProcessVotesBatch works (with or without accel)
|
|
votes := make([]Vote, 5)
|
|
for i := range votes {
|
|
votes[i] = Vote{
|
|
VoterID: [32]byte{byte(i)},
|
|
BlockID: [32]byte{1},
|
|
IsPreference: true,
|
|
}
|
|
}
|
|
|
|
processed, err := backend.ProcessVotesBatch(votes)
|
|
if err != nil {
|
|
t.Fatalf("ProcessVotesBatch failed: %v", err)
|
|
}
|
|
if processed != len(votes) {
|
|
t.Errorf("Expected %d processed, got %d", len(votes), processed)
|
|
}
|
|
|
|
// Test ComputeQuorum works
|
|
validators := []ValidatorInfo{
|
|
{ValidatorID: [32]byte{0}, Weight: 100},
|
|
{ValidatorID: [32]byte{1}, Weight: 100},
|
|
{ValidatorID: [32]byte{2}, Weight: 100},
|
|
}
|
|
|
|
result, err := backend.ComputeQuorum(votes, validators, 0.5)
|
|
if err != nil {
|
|
t.Fatalf("ComputeQuorum failed: %v", err)
|
|
}
|
|
if result == nil {
|
|
t.Fatal("ComputeQuorum returned nil result")
|
|
}
|
|
|
|
t.Logf("Quorum result: HasQuorum=%v, VotedWeight=%d, TotalWeight=%d",
|
|
result.HasQuorum, result.VotedWeight, result.TotalWeight)
|
|
}
|