Files

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)
}