feat: add test for mappers
This commit is contained in:
@@ -856,3 +856,534 @@ pub fn copy_channel_to_raw(source: types::Channel, destination: types::Channel)
|
|||||||
|
|
||||||
Ok((channel_to_raw_value(source), channel_to_raw_value(destination)))
|
Ok((channel_to_raw_value(source), channel_to_raw_value(destination)))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// CHANNEL
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn channel_round_trip_all_inputs() {
|
||||||
|
let channels = [
|
||||||
|
types::InputChannel::InA,
|
||||||
|
types::InputChannel::InB,
|
||||||
|
types::InputChannel::InC,
|
||||||
|
types::InputChannel::InD,
|
||||||
|
];
|
||||||
|
for ch in channels {
|
||||||
|
let raw = input_channel_to_raw(ch).unwrap();
|
||||||
|
let decoded = raw_to_channel(raw).unwrap();
|
||||||
|
assert_eq!(decoded, types::Channel::Input(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn channel_round_trip_all_outputs() {
|
||||||
|
let channels = [
|
||||||
|
types::OutputChannel::Out1,
|
||||||
|
types::OutputChannel::Out2,
|
||||||
|
types::OutputChannel::Out3,
|
||||||
|
types::OutputChannel::Out4,
|
||||||
|
types::OutputChannel::Out5,
|
||||||
|
types::OutputChannel::Out6,
|
||||||
|
types::OutputChannel::Out7,
|
||||||
|
types::OutputChannel::Out8,
|
||||||
|
];
|
||||||
|
for ch in channels {
|
||||||
|
let raw = output_channel_to_raw(ch).unwrap();
|
||||||
|
let decoded = raw_to_channel(raw).unwrap();
|
||||||
|
assert_eq!(decoded, types::Channel::Output(ch));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn channel_specific_raw_values() {
|
||||||
|
assert_eq!(channel_to_raw(types::Channel::Input(types::InputChannel::InA)).unwrap(), 0x00);
|
||||||
|
assert_eq!(channel_to_raw(types::Channel::Input(types::InputChannel::InD)).unwrap(), 0x03);
|
||||||
|
assert_eq!(channel_to_raw(types::Channel::Output(types::OutputChannel::Out1)).unwrap(), 0x04);
|
||||||
|
assert_eq!(channel_to_raw(types::Channel::Output(types::OutputChannel::Out8)).unwrap(), 0x0B);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// NAME
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn channel_name_round_trip() {
|
||||||
|
let name = "In ABCDE";
|
||||||
|
let raw = channel_name_to_raw(name).unwrap();
|
||||||
|
assert_eq!(raw.len(), client_constants::CHANNEL_NAME_LENGTH);
|
||||||
|
let decoded = raw_to_channel_name(&raw).unwrap();
|
||||||
|
assert_eq!(decoded, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn channel_name_padding_is_applied() {
|
||||||
|
let raw = channel_name_to_raw(" Kick ").unwrap();
|
||||||
|
assert_eq!(raw.len(), client_constants::CHANNEL_NAME_LENGTH);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// ATTACK / RELEASE / HOLD (simple offset-by-one mappings)
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attack_ms_mapping() {
|
||||||
|
assert_eq!(attack_ms_to_raw(1).unwrap(), 0);
|
||||||
|
assert_eq!(attack_ms_to_raw(999).unwrap(), 998);
|
||||||
|
assert_eq!(raw_to_attack_ms(0).unwrap(), 1);
|
||||||
|
assert_eq!(raw_to_attack_ms(998).unwrap(), 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn attack_ms_round_trip() {
|
||||||
|
for ms in [1u16, 50, 500, 999] {
|
||||||
|
let raw = attack_ms_to_raw(ms).unwrap();
|
||||||
|
assert_eq!(raw_to_attack_ms(raw).unwrap(), ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn release_ms_mapping() {
|
||||||
|
assert_eq!(release_ms_to_raw(10).unwrap(), 9);
|
||||||
|
assert_eq!(release_ms_to_raw(3000).unwrap(), 2999);
|
||||||
|
assert_eq!(raw_to_release_ms(9).unwrap(), 10);
|
||||||
|
assert_eq!(raw_to_release_ms(2999).unwrap(), 3000);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn release_ms_round_trip() {
|
||||||
|
for ms in [10u16, 100, 1500, 3000] {
|
||||||
|
let raw = release_ms_to_raw(ms).unwrap();
|
||||||
|
assert_eq!(raw_to_release_ms(raw).unwrap(), ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hold_ms_mapping() {
|
||||||
|
assert_eq!(hold_ms_to_raw(10).unwrap(), 9);
|
||||||
|
assert_eq!(hold_ms_to_raw(999).unwrap(), 998);
|
||||||
|
assert_eq!(raw_to_hold_ms(9).unwrap(), 10);
|
||||||
|
assert_eq!(raw_to_hold_ms(998).unwrap(), 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn hold_ms_round_trip() {
|
||||||
|
for ms in [10u16, 250, 999] {
|
||||||
|
let raw = hold_ms_to_raw(ms).unwrap();
|
||||||
|
assert_eq!(raw_to_hold_ms(raw).unwrap(), ms);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// GATE THRESHOLD
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gate_threshold_bounds() {
|
||||||
|
assert_eq!(gate_threshold_db_to_raw(-90.0).unwrap(), 0);
|
||||||
|
assert_eq!(gate_threshold_db_to_raw(0.0).unwrap(), 180); // (0 - -90) * 2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gate_threshold_round_trip() {
|
||||||
|
for db in [-90.0f32, -45.5, -20.25, 0.0] {
|
||||||
|
let raw = gate_threshold_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_gate_threshold_db(raw).unwrap();
|
||||||
|
assert!((decoded - (db * 2.0).round() / 2.0).abs() < 1e-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// EQ GAIN
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eq_gain_round_trip_endpoints() {
|
||||||
|
for db in [-12.0f32, 0.0, 12.0] {
|
||||||
|
let raw = eq_gain_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_eq_gain(raw).unwrap();
|
||||||
|
assert!((decoded - db).abs() < 1e-2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn eq_gain_round_trip_arbitrary() {
|
||||||
|
for db in [-7.3f32, 3.9, 11.99] {
|
||||||
|
let raw = eq_gain_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_eq_gain(raw).unwrap();
|
||||||
|
// allow one quantization step of tolerance
|
||||||
|
assert!((decoded - db).abs() <= eq_gain_step_db() + 1e-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// PEQ BAND
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn peq_band_output_all_bands() {
|
||||||
|
let bands = [
|
||||||
|
types::PEQBand::B1, types::PEQBand::B2, types::PEQBand::B3,
|
||||||
|
types::PEQBand::B4, types::PEQBand::B5, types::PEQBand::B6,
|
||||||
|
types::PEQBand::B7, types::PEQBand::B8, types::PEQBand::B9,
|
||||||
|
];
|
||||||
|
let out_channel = types::Channel::Output(types::OutputChannel::Out1);
|
||||||
|
for (i, band) in bands.into_iter().enumerate() {
|
||||||
|
assert_eq!(peq_band_to_raw(band, out_channel).unwrap(), i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn peq_band_input_allowed_bands() {
|
||||||
|
let bands = [
|
||||||
|
types::PEQBand::B1, types::PEQBand::B2, types::PEQBand::B3,
|
||||||
|
types::PEQBand::B4, types::PEQBand::B5, types::PEQBand::B6,
|
||||||
|
types::PEQBand::B7, types::PEQBand::B8,
|
||||||
|
];
|
||||||
|
let in_channel = types::Channel::Input(types::InputChannel::InA);
|
||||||
|
for (i, band) in bands.into_iter().enumerate() {
|
||||||
|
assert_eq!(peq_band_to_raw(band, in_channel).unwrap(), i as u8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// GAIN (channel gain)
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gain_round_trip_low_range() {
|
||||||
|
for db in [-60.0f32, -40.0, -20.5] {
|
||||||
|
let raw = gain_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_gain(raw).unwrap();
|
||||||
|
assert!((decoded - db).abs() < 1e-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gain_round_trip_high_range() {
|
||||||
|
for db in [-19.9f32, 0.0, 12.0] {
|
||||||
|
let raw = gain_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_gain(raw).unwrap();
|
||||||
|
assert!((decoded - db).abs() < 1e-3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn gain_split_boundary_is_low_step() {
|
||||||
|
let below = gain_db_to_raw(-20.5).unwrap();
|
||||||
|
let above = gain_db_to_raw(-19.9).unwrap();
|
||||||
|
assert_eq!(raw_to_gain(below).unwrap(), -20.5);
|
||||||
|
assert!((raw_to_gain(above).unwrap() - (-19.9)).abs() < 1e-3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// CONTIGUOUS FREQUENCY
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn frequency_round_trip_endpoints() {
|
||||||
|
for hz in [19.7f32, 20160.0] {
|
||||||
|
let raw = frequency_hz_to_raw(hz).unwrap();
|
||||||
|
let decoded = raw_to_frequency_hz(raw).unwrap();
|
||||||
|
// log scale quantization tolerance: within 1% of range
|
||||||
|
assert!((decoded - hz).abs() / hz < 0.01);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn frequency_round_trip_mid() {
|
||||||
|
let hz = 1000.0f32;
|
||||||
|
let raw = frequency_hz_to_raw(hz).unwrap();
|
||||||
|
let decoded = raw_to_frequency_hz(raw).unwrap();
|
||||||
|
assert!((decoded - hz).abs() / hz < 0.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// DISCRETE FREQUENCY
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn discrete_frequency_round_trip_all() {
|
||||||
|
let freqs = [
|
||||||
|
types::DiscreteFrequency::F20, types::DiscreteFrequency::F25,
|
||||||
|
types::DiscreteFrequency::F31_5, types::DiscreteFrequency::F40,
|
||||||
|
types::DiscreteFrequency::F50, types::DiscreteFrequency::F63,
|
||||||
|
types::DiscreteFrequency::F80, types::DiscreteFrequency::F100,
|
||||||
|
types::DiscreteFrequency::F125, types::DiscreteFrequency::F160,
|
||||||
|
types::DiscreteFrequency::F200, types::DiscreteFrequency::F250,
|
||||||
|
types::DiscreteFrequency::F315, types::DiscreteFrequency::F400,
|
||||||
|
types::DiscreteFrequency::F500, types::DiscreteFrequency::F630,
|
||||||
|
types::DiscreteFrequency::F800, types::DiscreteFrequency::F1000,
|
||||||
|
types::DiscreteFrequency::F1250, types::DiscreteFrequency::F1600,
|
||||||
|
types::DiscreteFrequency::F2000, types::DiscreteFrequency::F2500,
|
||||||
|
types::DiscreteFrequency::F3150, types::DiscreteFrequency::F4000,
|
||||||
|
types::DiscreteFrequency::F5000, types::DiscreteFrequency::F6300,
|
||||||
|
types::DiscreteFrequency::F8000, types::DiscreteFrequency::F10000,
|
||||||
|
types::DiscreteFrequency::F12500, types::DiscreteFrequency::F16000,
|
||||||
|
types::DiscreteFrequency::F20000,
|
||||||
|
];
|
||||||
|
for (i, f) in freqs.into_iter().enumerate() {
|
||||||
|
let raw = frequency_value_to_raw(f).unwrap();
|
||||||
|
assert_eq!(raw, i as u8);
|
||||||
|
assert_eq!(raw_to_frequency_value(raw).unwrap(), f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// CROSSOVER FILTER
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn crossover_filter_round_trip_all() {
|
||||||
|
let filters = [
|
||||||
|
types::CrossoverFilter::Bypass, types::CrossoverFilter::Bw6,
|
||||||
|
types::CrossoverFilter::Bl6, types::CrossoverFilter::Bw12,
|
||||||
|
types::CrossoverFilter::Bl12, types::CrossoverFilter::Lk12,
|
||||||
|
types::CrossoverFilter::Bw18, types::CrossoverFilter::Bl18,
|
||||||
|
types::CrossoverFilter::Bw24, types::CrossoverFilter::Bl24,
|
||||||
|
types::CrossoverFilter::Lk24, types::CrossoverFilter::Bw30,
|
||||||
|
types::CrossoverFilter::Bl30, types::CrossoverFilter::Bw36,
|
||||||
|
types::CrossoverFilter::Bl36, types::CrossoverFilter::Lk36,
|
||||||
|
types::CrossoverFilter::Bw42, types::CrossoverFilter::Bl42,
|
||||||
|
types::CrossoverFilter::Bw48, types::CrossoverFilter::Bl48,
|
||||||
|
types::CrossoverFilter::Lk48,
|
||||||
|
];
|
||||||
|
for (i, f) in filters.into_iter().enumerate() {
|
||||||
|
let raw = crossover_filter_to_raw(f).unwrap();
|
||||||
|
assert_eq!(raw, i as u8);
|
||||||
|
assert_eq!(raw_to_crossover_filter(raw).unwrap(), f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// PEQ Q
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn peq_q_round_trip_peak_filter() {
|
||||||
|
for q in [0.40f32, 1.0, 10.0, 128.0] {
|
||||||
|
let raw = peq_q_to_raw(q, types::PEQFilter::Peak).unwrap();
|
||||||
|
let decoded = raw_to_peq_q(raw).unwrap();
|
||||||
|
// log scale, allow 5% relative tolerance
|
||||||
|
assert!((decoded - q).abs() / q < 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn peq_q_round_trip_shelf_filter() {
|
||||||
|
for q in [0.40f32, 1.5, 3.0] {
|
||||||
|
let raw = peq_q_to_raw(q, types::PEQFilter::LowShelf).unwrap();
|
||||||
|
let decoded = raw_to_peq_q(raw).unwrap();
|
||||||
|
assert!((decoded - q).abs() / q < 0.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// PEQ FILTER
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn peq_filter_round_trip_all() {
|
||||||
|
let filters = [
|
||||||
|
types::PEQFilter::Peak, types::PEQFilter::LowShelf,
|
||||||
|
types::PEQFilter::HighShelf, types::PEQFilter::Lp6Db,
|
||||||
|
types::PEQFilter::Lp12Db, types::PEQFilter::Hp6Db,
|
||||||
|
types::PEQFilter::Hp12Db, types::PEQFilter::AllPass1,
|
||||||
|
types::PEQFilter::AllPass2,
|
||||||
|
];
|
||||||
|
for (i, f) in filters.into_iter().enumerate() {
|
||||||
|
let raw = peq_filter_to_raw(f).unwrap();
|
||||||
|
assert_eq!(raw, i as u8);
|
||||||
|
assert_eq!(raw_to_peq_filter(raw).unwrap(), f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// RATIO
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn ratio_round_trip_all() {
|
||||||
|
let ratios = [
|
||||||
|
types::Ratio::Ratio1_1, types::Ratio::Ratio1_1_1, types::Ratio::Ratio1_1_3,
|
||||||
|
types::Ratio::Ratio1_1_5, types::Ratio::Ratio1_1_7, types::Ratio::Ratio1_2,
|
||||||
|
types::Ratio::Ratio1_2_5, types::Ratio::Ratio1_3, types::Ratio::Ratio1_3_5,
|
||||||
|
types::Ratio::Ratio1_4, types::Ratio::Ratio1_5, types::Ratio::Ratio1_6,
|
||||||
|
types::Ratio::Ratio1_8, types::Ratio::Ratio1_10, types::Ratio::Ratio1_20,
|
||||||
|
types::Ratio::Limit,
|
||||||
|
];
|
||||||
|
for (i, r) in ratios.into_iter().enumerate() {
|
||||||
|
let raw = ratio_to_raw(r).unwrap();
|
||||||
|
assert_eq!(raw, i as u16);
|
||||||
|
assert_eq!(raw_to_ratio(raw).unwrap(), r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// KNEE
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn knee_is_identity_mapping() {
|
||||||
|
for db in [0u16, 6, 12] {
|
||||||
|
let raw = knee_db_to_raw(db).unwrap();
|
||||||
|
assert_eq!(raw, db);
|
||||||
|
assert_eq!(raw_to_knee_db(raw).unwrap(), db);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// THRESHOLD
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn threshold_round_trip() {
|
||||||
|
for db in [-90.0f32, -35.5, 0.0, 20.0] {
|
||||||
|
let raw = threshold_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_threshold_db(raw).unwrap();
|
||||||
|
assert!((decoded - (db * 2.0).round() / 2.0).abs() < 1e-4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// LINK
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_input_channels() {
|
||||||
|
let source = types::Channel::Input(types::InputChannel::InA);
|
||||||
|
let destinations = vec![
|
||||||
|
types::Channel::Input(types::InputChannel::InA),
|
||||||
|
types::Channel::Input(types::InputChannel::InC),
|
||||||
|
];
|
||||||
|
let mask = link_channels_to_raw(source, &destinations).unwrap();
|
||||||
|
assert_eq!(mask, 0b0000_0101);
|
||||||
|
|
||||||
|
let source_raw = channel_to_raw(source).unwrap();
|
||||||
|
let decoded = raw_to_link_channels(source_raw, mask).unwrap();
|
||||||
|
assert_eq!(decoded, destinations);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn link_output_channels() {
|
||||||
|
let source = types::Channel::Output(types::OutputChannel::Out2);
|
||||||
|
let destinations = vec![
|
||||||
|
types::Channel::Output(types::OutputChannel::Out2),
|
||||||
|
types::Channel::Output(types::OutputChannel::Out5),
|
||||||
|
types::Channel::Output(types::OutputChannel::Out8),
|
||||||
|
];
|
||||||
|
let mask = link_channels_to_raw(source, &destinations).unwrap();
|
||||||
|
assert_eq!(mask, (1 << 1) | (1 << 4) | (1 << 7));
|
||||||
|
|
||||||
|
let source_raw = channel_to_raw(source).unwrap();
|
||||||
|
let decoded = raw_to_link_channels(source_raw, mask).unwrap();
|
||||||
|
assert_eq!(decoded, destinations);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// MATRIX GAIN
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_gain_round_trip() {
|
||||||
|
for db in [-60.0f32, -30.25, -19.9, 0.0] {
|
||||||
|
let raw = matrix_gain_db_to_raw(db).unwrap();
|
||||||
|
let decoded = raw_to_matrix_gain(raw).unwrap();
|
||||||
|
assert!((decoded - db).abs() < 0.6);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// MATRIX
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_round_trip_all_inputs() {
|
||||||
|
let all = vec![
|
||||||
|
types::InputChannel::InA,
|
||||||
|
types::InputChannel::InB,
|
||||||
|
types::InputChannel::InC,
|
||||||
|
types::InputChannel::InD,
|
||||||
|
];
|
||||||
|
let raw = matrix_to_raw(&all).unwrap();
|
||||||
|
let mut decoded = raw_to_matrix(raw).unwrap();
|
||||||
|
decoded.sort_by_key(|c| format!("{:?}", c));
|
||||||
|
let mut expected = all.clone();
|
||||||
|
expected.sort_by_key(|c| format!("{:?}", c));
|
||||||
|
assert_eq!(decoded, expected);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_round_trip_subset() {
|
||||||
|
let subset = vec![types::InputChannel::InB, types::InputChannel::InD];
|
||||||
|
let raw = matrix_to_raw(&subset).unwrap();
|
||||||
|
let decoded = raw_to_matrix(raw).unwrap();
|
||||||
|
assert_eq!(decoded.len(), 2);
|
||||||
|
assert!(decoded.contains(&types::InputChannel::InB));
|
||||||
|
assert!(decoded.contains(&types::InputChannel::InD));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn matrix_empty() {
|
||||||
|
let raw = matrix_to_raw(&[]).unwrap();
|
||||||
|
assert_eq!(raw, 0);
|
||||||
|
assert!(raw_to_matrix(raw).unwrap().is_empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// DELAY
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delay_round_trip_milliseconds() {
|
||||||
|
for ms in [0.0f32, 340.0, 680.0] {
|
||||||
|
let raw = delay_to_raw(ms, types::DelayUnit::Millisecond).unwrap();
|
||||||
|
let decoded = raw_to_delay(raw, types::DelayUnit::Millisecond).unwrap();
|
||||||
|
assert!((decoded - ms).abs() < 5.0); // quantized to 8-bit resolution
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delay_round_trip_meters() {
|
||||||
|
for m in [0.0f32, 100.0, 233.58] {
|
||||||
|
let raw = delay_to_raw(m, types::DelayUnit::Meter).unwrap();
|
||||||
|
let decoded = raw_to_delay(raw, types::DelayUnit::Meter).unwrap();
|
||||||
|
assert!((decoded - m).abs() < 2.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delay_round_trip_feet() {
|
||||||
|
for ft in [0.0f32, 400.0, 766.329] {
|
||||||
|
let raw = delay_to_raw(ft, types::DelayUnit::Feet).unwrap();
|
||||||
|
let decoded = raw_to_delay(raw, types::DelayUnit::Feet).unwrap();
|
||||||
|
assert!((decoded - ft).abs() < 5.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
// COPY CHANNEL
|
||||||
|
// ------------------------------------------------------------------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn copy_channel_inputs() {
|
||||||
|
let source = types::Channel::Input(types::InputChannel::InA);
|
||||||
|
let dest = types::Channel::Input(types::InputChannel::InC);
|
||||||
|
let (raw_source, raw_dest) = copy_channel_to_raw(source, dest).unwrap();
|
||||||
|
assert_eq!(raw_source, 0x00);
|
||||||
|
assert_eq!(raw_dest, 0x02);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn copy_channel_outputs() {
|
||||||
|
let source = types::Channel::Output(types::OutputChannel::Out1);
|
||||||
|
let dest = types::Channel::Output(types::OutputChannel::Out8);
|
||||||
|
let (raw_source, raw_dest) = copy_channel_to_raw(source, dest).unwrap();
|
||||||
|
assert_eq!(raw_source, 0x04);
|
||||||
|
assert_eq!(raw_dest, 0x0B);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -146,3 +146,125 @@ pub fn password_to_raw(password: &str) -> Result<Vec<u8>, MapperError> {
|
|||||||
|
|
||||||
Ok(encode_ascii(password, client_constants::PASSWORD_LENGTH, client_constants::PASSWORD_PADDING)?)
|
Ok(encode_ascii(password, client_constants::PASSWORD_LENGTH, client_constants::PASSWORD_PADDING)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// -------- Delay Unit --------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delay_unit_round_trip() {
|
||||||
|
for unit in [
|
||||||
|
types::DelayUnit::Millisecond,
|
||||||
|
types::DelayUnit::Meter,
|
||||||
|
types::DelayUnit::Feet,
|
||||||
|
] {
|
||||||
|
let raw = delay_unit_to_raw(unit).unwrap();
|
||||||
|
assert_eq!(raw_to_delay_unit(raw).unwrap(), unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn delay_unit_rejects_invalid_raw() {
|
||||||
|
assert!(raw_to_delay_unit(0xff).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Preset Index --------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preset_index_to_raw_bounds() {
|
||||||
|
assert_eq!(preset_index_to_raw(0).unwrap(), 0);
|
||||||
|
assert_eq!(
|
||||||
|
preset_index_to_raw(client_constants::PRESET_INDEX_MAX).unwrap(),
|
||||||
|
client_constants::PRESET_INDEX_MAX as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preset_index_without_factory_to_raw_bounds() {
|
||||||
|
assert_eq!(preset_index_without_factory_to_raw(1).unwrap(), 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
preset_index_without_factory_to_raw(client_constants::PRESET_INDEX_MAX).unwrap(),
|
||||||
|
client_constants::PRESET_INDEX_MAX as u8 - 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preset_index_rejects_out_of_range() {
|
||||||
|
assert!(preset_index_to_raw(client_constants::PRESET_INDEX_MAX + 1).is_err());
|
||||||
|
assert!(preset_index_without_factory_to_raw(0).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Config Chunk --------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_chunk_bounds() {
|
||||||
|
assert_eq!(config_chunk_to_raw(0).unwrap(), 0);
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
config_chunk_to_raw(client_constants::CONFIG_CHUNK_INDEX_MAX).unwrap(),
|
||||||
|
client_constants::CONFIG_CHUNK_INDEX_MAX as u8,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn config_chunk_rejects_invalid() {
|
||||||
|
assert!(config_chunk_to_raw(client_constants::CONFIG_CHUNK_INDEX_MAX + 1).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Preset Name --------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preset_name_round_trip() {
|
||||||
|
let name = "DSP408 ";
|
||||||
|
|
||||||
|
let raw = preset_name_to_raw(name).unwrap();
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
raw_to_preset_name(&raw).unwrap(),
|
||||||
|
"DSP408",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn preset_name_rejects_invalid_length() {
|
||||||
|
assert!(preset_name_to_raw("short").is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Input Source --------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_source_round_trip() {
|
||||||
|
for source in [
|
||||||
|
types::InputType::Analog,
|
||||||
|
types::InputType::PinkNoise,
|
||||||
|
types::InputType::WhiteNoise,
|
||||||
|
types::InputType::SineWave,
|
||||||
|
] {
|
||||||
|
let raw = input_source_to_raw(source).unwrap();
|
||||||
|
assert_eq!(raw_to_input_source(raw).unwrap(), source);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn input_source_rejects_invalid_raw() {
|
||||||
|
assert!(raw_to_input_source(0xff).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// -------- Password --------
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn _password_to_raw() {
|
||||||
|
let raw = password_to_raw("1234").unwrap();
|
||||||
|
|
||||||
|
assert_eq!(raw, b"1234");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn password_rejects_invalid_length() {
|
||||||
|
assert!(password_to_raw("123").is_err());
|
||||||
|
assert!(password_to_raw("12345").is_err());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user