#![cfg(test)]
use crate::api::{LosslessConfig, LossyConfig};
use crate::validation::ValidationError;
#[test]
fn lossy_default_validates() {
let cfg = LossyConfig::new(1.0);
assert!(cfg.validate().is_ok());
}
#[test]
fn lossless_default_validates() {
let cfg = LosslessConfig::new();
assert!(cfg.validate().is_ok());
}
#[test]
fn lossy_typical_settings_validate() {
let cfg = LossyConfig::new(2.0).with_effort(7).with_patches(true);
assert!(cfg.validate().is_ok());
}
#[test]
fn lossy_distance_at_max_validates() {
let cfg = LossyConfig::new(25.0);
assert!(cfg.validate().is_ok());
}
#[test]
fn lossy_distance_just_above_zero_validates() {
let cfg = LossyConfig::new(0.000_1);
assert!(cfg.validate().is_ok());
}
#[test]
fn lossy_distance_zero_rejected() {
let cfg = LossyConfig::new(0.0);
let err = cfg.validate().unwrap_err();
assert!(matches!(err, ValidationError::DistanceOutOfRange { .. }));
}
#[test]
fn lossy_distance_negative_rejected() {
let cfg = LossyConfig::new(-1.0);
let err = cfg.validate().unwrap_err();
assert!(matches!(err, ValidationError::DistanceOutOfRange { .. }));
}
#[test]
fn lossy_distance_above_max_rejected() {
let cfg = LossyConfig::new(50.0);
match cfg.validate() {
Err(ValidationError::DistanceOutOfRange { value, valid }) => {
assert_eq!(value, 50.0);
assert_eq!(*valid.start(), 0.0);
assert_eq!(*valid.end(), 25.0);
}
other => panic!("expected DistanceOutOfRange, got {other:?}"),
}
}
#[test]
fn lossy_distance_nan_rejected() {
let cfg = LossyConfig::new(f32::NAN);
let err = cfg.validate().unwrap_err();
assert!(matches!(err, ValidationError::DistanceNotFinite { .. }));
}
#[test]
fn lossy_distance_infinite_rejected() {
let cfg = LossyConfig::new(f32::INFINITY);
let err = cfg.validate().unwrap_err();
assert!(matches!(err, ValidationError::DistanceNotFinite { .. }));
}
#[test]
fn lossy_effort_zero_rejected() {
for e in 1..=10u8 {
let cfg = LossyConfig::new(1.0).with_effort(e);
assert!(cfg.validate().is_ok(), "effort {e} should validate");
}
}
#[test]
fn lossless_effort_each_level_validates() {
for e in 1..=10u8 {
let cfg = LosslessConfig::new().with_effort(e);
assert!(cfg.validate().is_ok(), "effort {e} should validate");
}
}
#[cfg(feature = "butteraugli-loop")]
#[test]
fn lossy_butteraugli_iters_in_range_validates() {
for n in [0, 1, 4, 16] {
let cfg = LossyConfig::new(1.0).with_butteraugli_iters(n);
assert!(
cfg.validate().is_ok(),
"butteraugli_iters {n} should validate"
);
}
}
#[cfg(feature = "butteraugli-loop")]
#[test]
fn lossy_butteraugli_iters_too_high_rejected() {
let cfg = LossyConfig::new(1.0).with_butteraugli_iters(64);
match cfg.validate() {
Err(ValidationError::IterCountOutOfRange { name, value, valid }) => {
assert_eq!(name, "butteraugli_iters");
assert_eq!(value, 64);
assert_eq!(*valid.end(), 16);
}
other => panic!("expected IterCountOutOfRange, got {other:?}"),
}
}
#[cfg(feature = "ssim2-loop")]
#[test]
fn lossy_ssim2_iters_too_high_rejected() {
let cfg = LossyConfig::new(1.0).with_ssim2_iters(100);
match cfg.validate() {
Err(ValidationError::IterCountOutOfRange { name, value, .. }) => {
assert_eq!(name, "ssim2_iters");
assert_eq!(value, 100);
}
other => panic!("expected IterCountOutOfRange, got {other:?}"),
}
}
#[cfg(feature = "zensim-loop")]
#[test]
fn lossy_zensim_iters_too_high_rejected() {
let cfg = LossyConfig::new(1.0).with_zensim_iters(50);
match cfg.validate() {
Err(ValidationError::IterCountOutOfRange { name, value, .. }) => {
assert_eq!(name, "zensim_iters");
assert_eq!(value, 50);
}
other => panic!("expected IterCountOutOfRange, got {other:?}"),
}
}
#[cfg(all(feature = "butteraugli-loop", feature = "ssim2-loop"))]
#[test]
fn lossy_butteraugli_and_ssim2_mutually_exclusive() {
let cfg = LossyConfig::new(1.0)
.with_butteraugli_iters(2)
.with_ssim2_iters(2);
match cfg.validate() {
Err(ValidationError::QualityLoopMutuallyExclusive { first, second }) => {
assert_eq!(first, "butteraugli_iters");
assert_eq!(second, "ssim2_iters");
}
other => panic!("expected QualityLoopMutuallyExclusive, got {other:?}"),
}
}
#[cfg(all(feature = "butteraugli-loop", feature = "zensim-loop"))]
#[test]
fn lossy_butteraugli_and_zensim_mutually_exclusive() {
let cfg = LossyConfig::new(1.0)
.with_butteraugli_iters(2)
.with_zensim_iters(2);
match cfg.validate() {
Err(ValidationError::QualityLoopMutuallyExclusive { first, second }) => {
assert_eq!(first, "butteraugli_iters");
assert_eq!(second, "zensim_iters");
}
other => panic!("expected QualityLoopMutuallyExclusive, got {other:?}"),
}
}
#[cfg(all(feature = "ssim2-loop", feature = "zensim-loop"))]
#[test]
fn lossy_ssim2_and_zensim_mutually_exclusive() {
let mut cfg = LossyConfig::new(1.0)
.with_ssim2_iters(2)
.with_zensim_iters(2);
#[cfg(feature = "butteraugli-loop")]
{
cfg = cfg.with_butteraugli_iters(0);
}
match cfg.validate() {
Err(ValidationError::QualityLoopMutuallyExclusive { first, second }) => {
assert_eq!(first, "ssim2_iters");
assert_eq!(second, "zensim_iters");
}
other => panic!("expected QualityLoopMutuallyExclusive, got {other:?}"),
}
}
#[cfg(all(feature = "butteraugli-loop", feature = "ssim2-loop"))]
#[test]
fn lossy_only_one_quality_loop_validates() {
let cfg = LossyConfig::new(1.0)
.with_butteraugli_iters(2)
.with_ssim2_iters(0);
assert!(cfg.validate().is_ok());
}
#[cfg(feature = "__expert")]
mod expert {
use super::*;
use crate::effort::{LosslessInternalParams, LossyInternalParams};
#[test]
fn lossy_default_internal_params_validate() {
let p = LossyInternalParams::default();
assert!(p.validate().is_ok());
}
#[test]
fn lossy_internal_params_set_some_validates() {
let p = LossyInternalParams {
try_dct16: Some(false),
fine_grained_step: Some(2),
k_info_loss_mul_base: Some(1.3),
k_ac_quant: Some(0.8),
..Default::default()
};
assert!(p.validate().is_ok());
}
#[test]
fn lossy_fine_grained_step_zero_rejected() {
let p = LossyInternalParams {
fine_grained_step: Some(0),
..Default::default()
};
match p.validate() {
Err(ValidationError::FineGrainedStepOutOfRange { value, .. }) => {
assert_eq!(value, 0);
}
other => panic!("expected FineGrainedStepOutOfRange, got {other:?}"),
}
}
#[test]
fn lossy_fine_grained_step_too_high_rejected() {
let p = LossyInternalParams {
fine_grained_step: Some(99),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::FineGrainedStepOutOfRange { value: 99, .. }
));
}
#[test]
fn lossy_k_info_loss_mul_base_zero_rejected() {
let p = LossyInternalParams {
k_info_loss_mul_base: Some(0.0),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::KInfoLossMulBaseInvalid { .. }
));
}
#[test]
fn lossy_k_info_loss_mul_base_negative_rejected() {
let p = LossyInternalParams {
k_info_loss_mul_base: Some(-1.0),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::KInfoLossMulBaseInvalid { .. }
));
}
#[test]
fn lossy_k_info_loss_mul_base_nan_rejected() {
let p = LossyInternalParams {
k_info_loss_mul_base: Some(f32::NAN),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::KInfoLossMulBaseInvalid { .. }
));
}
#[test]
fn lossy_k_ac_quant_zero_rejected() {
let p = LossyInternalParams {
k_ac_quant: Some(0.0),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::KAcQuantInvalid { .. }
));
}
#[test]
fn lossy_k_ac_quant_infinite_rejected() {
let p = LossyInternalParams {
k_ac_quant: Some(f32::INFINITY),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::KAcQuantInvalid { .. }
));
}
#[test]
fn lossless_default_internal_params_validate() {
let p = LosslessInternalParams::default();
assert!(p.validate().is_ok());
}
#[test]
fn lossless_internal_params_set_some_validates() {
let p = LosslessInternalParams {
nb_rcts_to_try: Some(7),
wp_num_param_sets: Some(2),
tree_max_buckets: Some(96),
tree_num_properties: Some(7),
tree_threshold_base: Some(89.0),
tree_sample_fraction: Some(0.5),
tree_max_samples_fixed: Some(0),
};
assert!(p.validate().is_ok());
}
#[test]
fn lossless_nb_rcts_too_high_rejected() {
let p = LosslessInternalParams {
nb_rcts_to_try: Some(50),
..Default::default()
};
match p.validate() {
Err(ValidationError::NbRctsToTryOutOfRange { value, valid }) => {
assert_eq!(value, 50);
assert_eq!(*valid.end(), 19);
}
other => panic!("expected NbRctsToTryOutOfRange, got {other:?}"),
}
}
#[test]
fn lossless_wp_num_param_sets_too_high_rejected() {
let p = LosslessInternalParams {
wp_num_param_sets: Some(8),
..Default::default()
};
match p.validate() {
Err(ValidationError::WpNumParamSetsOutOfRange { value, valid }) => {
assert_eq!(value, 8);
assert_eq!(*valid.end(), 5);
}
other => panic!("expected WpNumParamSetsOutOfRange, got {other:?}"),
}
}
#[test]
fn lossless_tree_max_buckets_zero_rejected() {
let p = LosslessInternalParams {
tree_max_buckets: Some(0),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::TreeMaxBucketsZero
));
}
#[test]
fn lossless_tree_num_properties_too_high_rejected() {
let p = LosslessInternalParams {
tree_num_properties: Some(99),
..Default::default()
};
match p.validate() {
Err(ValidationError::TreeNumPropertiesOutOfRange { value, valid }) => {
assert_eq!(value, 99);
assert_eq!(*valid.end(), 16);
}
other => panic!("expected TreeNumPropertiesOutOfRange, got {other:?}"),
}
}
#[test]
fn lossless_tree_threshold_negative_rejected() {
let p = LosslessInternalParams {
tree_threshold_base: Some(-10.0),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::TreeThresholdBaseInvalid { .. }
));
}
#[test]
fn lossless_tree_threshold_nan_rejected() {
let p = LosslessInternalParams {
tree_threshold_base: Some(f32::NAN),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::TreeThresholdBaseInvalid { .. }
));
}
#[test]
fn lossless_tree_sample_fraction_above_one_rejected() {
let p = LosslessInternalParams {
tree_sample_fraction: Some(1.5),
..Default::default()
};
match p.validate() {
Err(ValidationError::TreeSampleFractionOutOfRange { value, valid }) => {
assert_eq!(value, 1.5);
assert_eq!(*valid.end(), 1.0);
}
other => panic!("expected TreeSampleFractionOutOfRange, got {other:?}"),
}
}
#[test]
fn lossless_tree_sample_fraction_negative_rejected() {
let p = LosslessInternalParams {
tree_sample_fraction: Some(-0.1),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::TreeSampleFractionOutOfRange { .. }
));
}
#[test]
fn lossless_tree_sample_fraction_nan_rejected() {
let p = LosslessInternalParams {
tree_sample_fraction: Some(f32::NAN),
..Default::default()
};
assert!(matches!(
p.validate().unwrap_err(),
ValidationError::TreeSampleFractionOutOfRange { .. }
));
}
#[test]
fn lossy_config_validates_profile_override() {
let bad = LossyInternalParams {
fine_grained_step: Some(0),
..Default::default()
};
let cfg = LossyConfig::new(1.0).with_internal_params(bad);
assert!(matches!(
cfg.validate().unwrap_err(),
ValidationError::FineGrainedStepOutOfRange { value: 0, .. }
));
}
#[test]
fn lossless_config_validates_profile_override() {
let bad = LosslessInternalParams {
tree_max_buckets: Some(0),
..Default::default()
};
let cfg = LosslessConfig::new().with_internal_params(bad);
assert!(matches!(
cfg.validate().unwrap_err(),
ValidationError::TreeMaxBucketsZero
));
}
#[test]
fn validate_does_not_alter_encode_path() {
let cfg = LossyConfig::new(50.0);
assert!(cfg.validate().is_err());
assert_eq!(cfg.distance(), 50.0);
}
}