use crate::tuner::*;
#[test]
fn test_tuner_features_validate() {
let features = TunerFeatures::builder().build();
assert!(features.validate().is_ok());
let mut bad_features = features.clone();
bad_features.model_params_b = f32::NAN;
assert!(bad_features.validate().is_err());
}
#[test]
fn test_tuner_error_display() {
let err = TunerError::InvalidFeature("test".to_string());
assert!(format!("{}", err).contains("Invalid feature"));
let err = TunerError::InsufficientData(5);
assert!(format!("{}", err).contains("Insufficient"));
let err = TunerError::Serialization("test".to_string());
assert!(format!("{}", err).contains("Serialization"));
let err = TunerError::ModelNotFound;
assert!(format!("{}", err).contains("not found"));
let err = TunerError::PredictionFailed("test".to_string());
assert!(format!("{}", err).contains("Prediction failed"));
}
#[test]
fn test_throughput_regressor_predict_raw() {
let regressor = ThroughputRegressor::new();
let features = TunerFeatures::builder().batch_size(4).build();
let vec = features.to_vector();
let raw = regressor.predict_raw(&vec);
assert!(raw > 0.0);
}
#[test]
fn test_brick_tuner_recommend() {
let tuner = BrickTuner::new();
let features = TunerFeatures::builder().model_params_b(1.5).batch_size(4).build();
let rec = tuner.recommend(&features);
assert!(rec.throughput.predicted_tps > 0.0);
assert!(!rec.suggested_experiments.is_empty());
}
#[test]
fn test_experiment_suggestion_display() {
let exp = ExperimentSuggestion::IncreaseBatchSize { from: 1, to: 4 };
assert!(format!("{}", exp).contains("Increase batch size"));
let exp = ExperimentSuggestion::EnableCudaGraphs;
assert!(format!("{}", exp).contains("CUDA graphs"));
let exp = ExperimentSuggestion::TryKernel { kernel: KernelType::BatchedQ4K };
assert!(format!("{}", exp).contains("kernel"));
let exp = ExperimentSuggestion::ReduceSequenceLength { factor: 0.5 };
assert!(format!("{}", exp).contains("sequence"));
let exp = ExperimentSuggestion::EnableMultiKvCache { count: 4 };
assert!(format!("{}", exp).contains("KV"));
}
#[test]
fn test_tuner_data_collector() {
let collector = TunerDataCollector::new();
assert!(collector.is_empty());
assert_eq!(collector.len(), 0);
assert!(collector.samples().is_empty());
}
#[test]
fn test_feature_extractor_default() {
let extractor = FeatureExtractor::new();
assert!(extractor.hardware.is_none());
}
#[test]
fn test_feature_extractor_debug() {
let extractor = FeatureExtractor::new();
let debug_str = format!("{:?}", extractor);
assert!(debug_str.contains("FeatureExtractor"));
}
#[test]
fn test_chrono_lite_now() {
let timestamp = crate::tuner::chrono_lite_now();
let parsed: u64 = timestamp.parse().expect("Should be a number");
assert!(parsed > 0);
}
#[test]
fn test_pad_right() {
assert_eq!(crate::tuner::pad_right("test", 10), "test ");
assert_eq!(crate::tuner::pad_right("longstring", 5), "longs");
}
#[test]
fn test_validation_infinite_features() {
let features = TunerFeatures { model_params_b: f32::INFINITY, ..Default::default() };
let result = features.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("Infinite"));
}
#[test]
fn test_validation_out_of_range() {
let features = TunerFeatures {
batch_size_norm: 2.0, ..Default::default()
};
let result = features.validate();
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("outside [0, 1]"));
}
#[test]
fn test_validation_bad_quant_onehot() {
let features = TunerFeatures {
quant_type_onehot: [0.5, 0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ..Default::default()
};
assert!(features.validate().is_ok());
let features2 = TunerFeatures {
quant_type_onehot: [0.5, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], ..Default::default()
};
let result = features2.validate();
assert!(result.is_err());
}
#[test]
fn test_validation_bad_kernel_onehot() {
let mut features = TunerFeatures {
kernel_type_onehot: [0.0; 16], ..Default::default()
};
assert!(features.validate().is_ok());
features.kernel_type_onehot[0] = 0.5;
let result = features.validate();
assert!(result.is_err());
}
#[test]
fn test_builder_gpu_l2_cache_mb() {
let features = TunerFeatures::builder()
.gpu_l2_cache_mb(96.0) .build();
assert!((features.gpu_l2_cache_norm - 0.75).abs() < 0.01);
}
#[test]
fn test_builder_is_zero_copy() {
let features_enabled = TunerFeatures::builder().is_zero_copy(true).build();
assert_eq!(features_enabled.is_zero_copy, 1.0);
let features_disabled = TunerFeatures::builder().is_zero_copy(false).build();
assert_eq!(features_disabled.is_zero_copy, 0.0);
}
#[test]
fn test_builder_hardware() {
use crate::hardware::{GpuBackend, GpuCapability};
let gpu = GpuCapability {
vendor: "NVIDIA".to_string(),
model: "Test GPU".to_string(),
backend: GpuBackend::Cuda,
compute_capability: Some("8.9".to_string()),
peak_tflops_fp32: 100.0,
peak_tflops_tensor: Some(400.0),
memory_bw_gbps: 1000.0,
vram_gb: 24.0,
};
let features = TunerFeatures::builder()
.gpu_mem_bw_gbs(gpu.memory_bw_gbps as f32)
.gpu_compute_tflops(gpu.peak_tflops_fp32 as f32)
.build();
assert!((features.gpu_mem_bw_norm - (1000.0 / 3000.0)).abs() < 0.01);
assert!((features.gpu_compute_norm - 0.2).abs() < 0.01);
}
#[test]
fn test_brick_tuner_train() {
let mut tuner = BrickTuner::new();
let data: Vec<(TunerFeatures, f32)> = (0..15)
.map(|i| {
let features = TunerFeatures::builder()
.batch_size((i % 4) as u32 + 1)
.model_params_b(1.5 + (i as f32) * 0.1)
.build();
(features, 100.0 + (i as f32) * 10.0)
})
.collect();
let result = tuner.train(&data);
assert!(result.is_ok());
assert_eq!(tuner.sample_count, 15);
}
#[test]
fn test_brick_tuner_train_insufficient_data() {
let mut tuner = BrickTuner::new();
let data: Vec<(TunerFeatures, f32)> = (0..5)
.map(|i| {
let features = TunerFeatures::builder().batch_size(i as u32 + 1).build();
(features, 100.0)
})
.collect();
let result = tuner.train(&data);
assert!(result.is_err());
assert!(matches!(result.unwrap_err(), TunerError::InsufficientData(5)));
}