use autoeq::LossType;
use autoeq::cli::{Args, PeqModel};
use autoeq::de::Strategy;
use autoeq::optim::optimize_filters;
use autoeq::workflow::{initial_guess, setup_bounds};
use clap::Parser;
use ndarray::Array1;
use std::str::FromStr;
fn create_test_objective_data() -> autoeq::optim::ObjectiveData {
let freqs = Array1::from(vec![100.0, 1000.0, 10000.0]);
let target = Array1::from(vec![1.0, 1.0, 1.0]); let deviation = Array1::from(vec![0.5, 0.5, 0.5]);
autoeq::optim::ObjectiveData {
freqs: freqs.clone(),
target,
deviation,
input_curve: None,
srate: 48000.0,
min_spacing_oct: 0.5,
spacing_weight: 20.0,
max_db: 3.0,
min_db: 1.0,
min_freq: 60.0,
max_freq: 16000.0,
peq_model: PeqModel::Pk,
loss_type: LossType::SpeakerFlat,
speaker_score_data: None,
headphone_score_data: None,
drivers_data: None,
fixed_crossover_freqs: None,
penalty_w_ceiling: 0.0,
penalty_w_spacing: 0.0,
penalty_w_mingain: 0.0,
integrality: None,
multi_objective: None,
smooth: false,
smooth_n: 3,
max_boost_envelope: None,
min_cut_envelope: None,
}
}
#[test]
fn test_tolerance_parameter_affects_optimization() {
let mut args_high_tol = Args::parse_from([
"autoeq-test",
"--algo",
"autoeq:de",
"--tolerance",
"0.1", "--num-filters",
"2",
]);
let mut args_low_tol = Args::parse_from([
"autoeq-test",
"--algo",
"autoeq:de",
"--tolerance",
"0.0001", "--num-filters",
"2",
]);
assert_ne!(args_high_tol.tolerance, args_low_tol.tolerance);
assert_eq!(args_high_tol.tolerance, 0.1);
assert_eq!(args_low_tol.tolerance, 0.0001);
args_high_tol.strategy = "best1bin".to_string();
args_low_tol.strategy = "best1bin".to_string();
args_high_tol.maxeval = 50; args_low_tol.maxeval = 50;
args_high_tol.population = 20; args_low_tol.population = 20;
let objective_data = create_test_objective_data();
let (lower_bounds, upper_bounds) = setup_bounds(&args_high_tol);
let mut x1 = initial_guess(&args_high_tol, &lower_bounds, &upper_bounds);
let mut x2 = initial_guess(&args_low_tol, &lower_bounds, &upper_bounds);
let result1 = optimize_filters(
&mut x1,
&lower_bounds,
&upper_bounds,
objective_data.clone(),
&args_high_tol,
);
let result2 = optimize_filters(
&mut x2,
&lower_bounds,
&upper_bounds,
objective_data,
&args_low_tol,
);
assert!(
result1.is_ok(),
"High tolerance optimization failed: {:?}",
result1
);
assert!(
result2.is_ok(),
"Low tolerance optimization failed: {:?}",
result2
);
}
#[test]
fn test_strategy_parameter_affects_optimization() {
let strategies = ["best1bin", "rand1bin", "currenttobest1bin"];
for strategy_name in strategies.iter() {
let mut args = Args::parse_from([
"autoeq-test",
"--algo",
"autoeq:de",
"--strategy",
strategy_name,
"--num-filters",
"2",
]);
args.population = 10; args.maxeval = 30;
assert_eq!(args.strategy, *strategy_name);
let strategy = Strategy::from_str(&args.strategy);
assert!(
strategy.is_ok(),
"Failed to parse strategy: {}",
strategy_name
);
let objective_data = create_test_objective_data();
let (lower_bounds, upper_bounds) = setup_bounds(&args);
let mut x = initial_guess(&args, &lower_bounds, &upper_bounds);
let result = optimize_filters(&mut x, &lower_bounds, &upper_bounds, objective_data, &args);
assert!(
result.is_ok(),
"Strategy {} optimization failed: {:?}",
strategy_name,
result
);
}
}
#[test]
fn test_recombination_parameter_affects_optimization() {
let recombination_values = [0.1, 0.5, 0.9];
for recomb in recombination_values.iter() {
let mut args = Args::parse_from([
"autoeq-test",
"--algo",
"autoeq:de",
"--recombination",
&recomb.to_string(),
"--num-filters",
"2",
]);
args.population = 10; args.maxeval = 30;
assert_eq!(args.recombination, *recomb);
let objective_data = create_test_objective_data();
let (lower_bounds, upper_bounds) = setup_bounds(&args);
let mut x = initial_guess(&args, &lower_bounds, &upper_bounds);
let result = optimize_filters(&mut x, &lower_bounds, &upper_bounds, objective_data, &args);
assert!(
result.is_ok(),
"Recombination {} optimization failed: {:?}",
recomb,
result
);
}
}
#[test]
fn test_adaptive_strategy_with_weights() {
let mut args = Args::parse_from([
"autoeq-test",
"--algo",
"autoeq:de",
"--strategy",
"adaptivebin",
"--adaptive-weight-f",
"0.8",
"--adaptive-weight-cr",
"0.7",
"--num-filters",
"2",
]);
args.population = 15; args.maxeval = 40;
assert_eq!(args.strategy, "adaptivebin");
assert_eq!(args.adaptive_weight_f, 0.8);
assert_eq!(args.adaptive_weight_cr, 0.7);
let objective_data = create_test_objective_data();
let (lower_bounds, upper_bounds) = setup_bounds(&args);
let mut x = initial_guess(&args, &lower_bounds, &upper_bounds);
let result = optimize_filters(&mut x, &lower_bounds, &upper_bounds, objective_data, &args);
assert!(
result.is_ok(),
"Adaptive strategy optimization failed: {:?}",
result
);
}
#[test]
fn test_parameter_validation_with_de_algorithm() {
let test_cases = vec![
(
vec![
"autoeq-test",
"--algo",
"autoeq:de",
"--strategy",
"invalid",
],
"Invalid DE strategy",
),
(
vec!["autoeq-test", "--algo", "autoeq:de", "--tolerance", "0"],
"Tolerance must be > 0",
),
(
vec!["autoeq-test", "--algo", "autoeq:de", "--atolerance=-0.1"],
"Absolute tolerance must be >= 0",
),
(
vec![
"autoeq-test",
"--algo",
"autoeq:de",
"--adaptive-weight-f",
"1.1",
],
"Adaptive weight for F must be between 0.0 and 1.0",
),
(
vec![
"autoeq-test",
"--algo",
"autoeq:de",
"--adaptive-weight-cr=-0.1",
],
"Adaptive weight for CR must be between 0.0 and 1.0",
),
];
for (args_vec, expected_error) in test_cases {
let args = Args::parse_from(args_vec.clone());
let result = autoeq::cli::validate_args(&args);
assert!(
result.is_err(),
"Expected validation error for args: {:?}",
args_vec
);
assert!(
result.unwrap_err().contains(expected_error),
"Error message should contain: {}",
expected_error
);
}
}