use embedded_hal_mock::{ i2c::Transaction as I2cTrans, pin::{Mock as PinMock, State as PinState, Transaction as PinTrans}, }; use nb::block; use si4703::{ Error, ErrorWithPin, SeekDirection, SeekFmImpulseThreshold as Cnt, SeekMode, SeekSnrThreshold as Snr, }; mod common; use self::common::{destroy, new_si4703, BitFlags as BF, DEV_ADDR}; #[macro_export] macro_rules! config_seek_test { ($name:ident, $sysconfig2:expr, $sysconfig3:expr $(, $value:expr)*) => { write_test!( $name, 16, 3, $sysconfig2, 4, $sysconfig3, configure_seek, $($value),* ); }; } config_seek_test!(default, 0, 0, 0, Snr::default(), Cnt::default()); config_seek_test!(rssi_th, 0xAB00_u16, 0, 0xAB, Snr::default(), Cnt::default()); config_seek_test!(snr_th, 0, 7 << 4, 0, Snr::Enabled(7), Cnt::default()); config_seek_test!(fm_impulse_th, 0, 15, 0, Snr::default(), Cnt::Enabled(15)); macro_rules! invalid_config_seek_test { ($name:ident, $snr:expr, $cnt:expr) => { #[test] fn $name() { let mut dev = new_si4703(&[]); assert_error!(dev.configure_seek(0, $snr, $cnt), Error::InvalidInputData); } }; } invalid_config_seek_test!(invalid_snr_th_too_small, Snr::Enabled(0), Cnt::default()); invalid_config_seek_test!(invalid_snr_th_too_big, Snr::Enabled(8), Cnt::default()); invalid_config_seek_test!(invalid_fm_imp_th_too_small, Snr::default(), Cnt::Enabled(0)); invalid_config_seek_test!(invalid_fm_imp_th_too_big, Snr::default(), Cnt::Enabled(16)); macro_rules! seek_test { ($name:ident, $mode:ident, $direction:ident, $powercfg:expr) => { #[test] fn $name() { let powercfg = $powercfg | BF::SEEK; let mut found_data = [0; 32]; found_data[0] = (BF::STC >> 8) as u8; found_data[1] = BF::STC as u8; let mut seeking_data = [0; 32]; seeking_data[16] = (powercfg >> 8) as u8; seeking_data[17] = powercfg as u8; let mut seeking_found_data = [0; 32]; seeking_found_data[0] = (BF::STC >> 8) as u8; seeking_found_data[1] = BF::STC as u8; seeking_found_data[16] = (powercfg >> 8) as u8; seeking_found_data[17] = powercfg as u8; let transactions = [ I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), I2cTrans::write(DEV_ADDR, vec![(powercfg >> 8) as u8, powercfg as u8]), I2cTrans::read(DEV_ADDR, seeking_data.to_vec()), I2cTrans::read(DEV_ADDR, seeking_found_data.to_vec()), I2cTrans::write( DEV_ADDR, vec![ ((powercfg & !BF::SEEK) >> 8) as u8, (powercfg & !BF::SEEK) as u8, ], ), I2cTrans::read(DEV_ADDR, found_data.to_vec()), I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), ]; let mut dev = new_si4703(&transactions); block!(dev.seek(SeekMode::$mode, SeekDirection::$direction)).unwrap(); destroy(dev); } }; } seek_test!(seek_nowrap_down, NoWrap, Down, 0); seek_test!(seek_wrap_down, Wrap, Down, BF::SKMODE); seek_test!(seek_nowrap_up, NoWrap, Up, BF::SEEKUP); seek_test!(seek_wrap_up, Wrap, Up, BF::SKMODE | BF::SEEKUP); #[test] fn can_seek() { let mut found_data = [0; 32]; found_data[0] = (BF::STC >> 8) as u8; found_data[1] = BF::STC as u8; let mut seeking_data = [0; 32]; seeking_data[16] = (BF::SEEK >> 8) as u8; seeking_data[17] = BF::SEEK as u8; let mut seeking_found_data = [0; 32]; seeking_found_data[0] = (BF::STC >> 8) as u8; seeking_found_data[1] = BF::STC as u8; seeking_found_data[16] = (BF::SEEK >> 8) as u8; seeking_found_data[17] = BF::SEEK as u8; let transactions = [ I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), I2cTrans::write(DEV_ADDR, vec![(BF::SEEK >> 8) as u8, BF::SEEK as u8]), I2cTrans::read(DEV_ADDR, seeking_data.to_vec()), I2cTrans::read(DEV_ADDR, seeking_found_data.to_vec()), I2cTrans::write(DEV_ADDR, vec![0, 0]), I2cTrans::read(DEV_ADDR, found_data.to_vec()), I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), ]; let mut dev = new_si4703(&transactions); block!(dev.seek(SeekMode::NoWrap, SeekDirection::Down)).unwrap(); destroy(dev); } fn fail_seeking_test(seeking_found_statusrssi: u16) { let mut found_data = [0; 32]; found_data[0] = (BF::STC >> 8) as u8; found_data[1] = BF::STC as u8; let mut seeking_data = [0; 32]; seeking_data[16] = (BF::SEEK >> 8) as u8; seeking_data[17] = BF::SEEK as u8; let mut seeking_found_data = [0; 32]; seeking_found_data[0] = (seeking_found_statusrssi >> 8) as u8; seeking_found_data[1] = seeking_found_statusrssi as u8; seeking_found_data[16] = (BF::SEEK >> 8) as u8; seeking_found_data[17] = BF::SEEK as u8; let transactions = [ I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), I2cTrans::write(DEV_ADDR, vec![(BF::SEEK >> 8) as u8, BF::SEEK as u8]), I2cTrans::read(DEV_ADDR, seeking_data.to_vec()), I2cTrans::read(DEV_ADDR, seeking_found_data.to_vec()), I2cTrans::write(DEV_ADDR, vec![0, 0]), I2cTrans::read(DEV_ADDR, found_data.to_vec()), I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), ]; let mut dev = new_si4703(&transactions); assert_error!( block!(dev.seek(SeekMode::NoWrap, SeekDirection::Down)), Error::SeekFailed ); destroy(dev); } #[test] fn can_fail_seeking() { let statusrssi = BF::STC | BF::SF_BL; fail_seeking_test(statusrssi); } #[test] fn can_fail_seeking_afc_railed() { let statusrssi = BF::STC | BF::AFCRL; fail_seeking_test(statusrssi); } #[test] fn can_seek_with_stc_int_pin() { let mut found_data = [0; 32]; found_data[0] = (BF::STC >> 8) as u8; found_data[1] = BF::STC as u8; let mut seeking_data = [0; 32]; seeking_data[16] = (BF::SEEK >> 8) as u8; seeking_data[17] = BF::SEEK as u8; seeking_data[20] = (BF::STCIEN >> 8) as u8; seeking_data[21] = BF::STCIEN as u8 | 1 << 2; let mut seeking_found_data = [0; 32]; seeking_found_data[0] = (BF::STC >> 8) as u8; seeking_found_data[1] = BF::STC as u8; seeking_found_data[16] = (BF::SEEK >> 8) as u8; seeking_found_data[17] = BF::SEEK as u8; let transactions = [ I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), I2cTrans::write( DEV_ADDR, vec![ (BF::SEEK >> 8) as u8, BF::SEEK as u8, 0, 0, (BF::STCIEN >> 8) as u8, BF::STCIEN as u8 | 1 << 2, ], ), I2cTrans::read(DEV_ADDR, seeking_data.to_vec()), // this time STC bit is (incorrectly) not set I2cTrans::read(DEV_ADDR, seeking_found_data.to_vec()), I2cTrans::write(DEV_ADDR, vec![0, 0]), I2cTrans::read(DEV_ADDR, found_data.to_vec()), I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), ]; let pin_trans = [ PinTrans::get(PinState::High), PinTrans::get(PinState::Low), // this time STC bit is (incorrectly) not set PinTrans::get(PinState::Low), ]; let mut pin = PinMock::new(&pin_trans); let mut dev = new_si4703(&transactions); block!(dev.seek_with_stc_int_pin(SeekMode::NoWrap, SeekDirection::Down, &pin)).unwrap(); destroy(dev); pin.done() } fn fail_seeking_with_stc_int_pin_test(seeking_found_statusrssi: u16) { let mut found_data = [0; 32]; found_data[0] = (BF::STC >> 8) as u8; found_data[1] = BF::STC as u8; let mut seeking_data = [0; 32]; seeking_data[16] = (BF::SEEK >> 8) as u8; seeking_data[17] = BF::SEEK as u8; seeking_data[20] = (BF::STCIEN >> 8) as u8; seeking_data[21] = BF::STCIEN as u8 | 1 << 2; let mut seeking_found_data = [0; 32]; seeking_found_data[0] = (seeking_found_statusrssi >> 8) as u8; seeking_found_data[1] = seeking_found_statusrssi as u8; seeking_found_data[16] = (BF::SEEK >> 8) as u8; seeking_found_data[17] = BF::SEEK as u8; let transactions = [ I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), I2cTrans::write( DEV_ADDR, vec![ (BF::SEEK >> 8) as u8, BF::SEEK as u8, 0, 0, (BF::STCIEN >> 8) as u8, BF::STCIEN as u8 | 1 << 2, ], ), I2cTrans::read(DEV_ADDR, seeking_data.to_vec()), // this time STC bit is (incorrectly) not set I2cTrans::read(DEV_ADDR, seeking_found_data.to_vec()), I2cTrans::write(DEV_ADDR, vec![0, 0]), I2cTrans::read(DEV_ADDR, found_data.to_vec()), I2cTrans::read(DEV_ADDR, [0; 32].to_vec()), ]; let pin_trans = [ PinTrans::get(PinState::High), PinTrans::get(PinState::Low), // this time STC bit is (incorrectly) not set PinTrans::get(PinState::Low), ]; let mut pin = PinMock::new(&pin_trans); let mut dev = new_si4703(&transactions); assert_error!( block!(dev.seek_with_stc_int_pin(SeekMode::NoWrap, SeekDirection::Down, &pin)), ErrorWithPin::SeekFailed ); destroy(dev); pin.done(); } #[test] fn can_fail_seeking_with_stc_int_pin() { let statusrssi = BF::STC | BF::SF_BL; fail_seeking_with_stc_int_pin_test(statusrssi); } #[test] fn can_fail_seeking_with_stc_int_pin_afc_railed() { let statusrssi = BF::STC | BF::AFCRL; fail_seeking_with_stc_int_pin_test(statusrssi); }