use super::*;
#[test]
fn test_rds_syndrome_zero() {
let word = 0u32;
let syn = rds_syndrome(word);
assert_eq!(syn, 0, "Syndrome of zero should be zero");
}
#[test]
fn test_rds_syndrome_offset_word_a() {
let syn = rds_syndrome(OFFSET_WORD_A as u32);
assert_eq!(
syn, SYNDROME_A,
"Syndrome of OFFSET_WORD_A should be SYNDROME_A"
);
}
#[test]
fn test_rds_syndrome_offset_word_b() {
let syn = rds_syndrome(OFFSET_WORD_B as u32);
assert_eq!(
syn, SYNDROME_B,
"Syndrome of OFFSET_WORD_B should be SYNDROME_B"
);
}
#[test]
fn test_rds_syndrome_offset_word_c() {
let syn = rds_syndrome(OFFSET_WORD_C as u32);
assert_eq!(
syn, SYNDROME_C,
"Syndrome of OFFSET_WORD_C should be SYNDROME_C"
);
}
#[test]
fn test_rds_syndrome_offset_word_d() {
let syn = rds_syndrome(OFFSET_WORD_D as u32);
assert_eq!(
syn, SYNDROME_D,
"Syndrome of OFFSET_WORD_D should be SYNDROME_D"
);
}
#[test]
fn test_rds_syndrome_data_only() {
let data = 0x1234u16;
let word26 = (data as u32) << 10;
let syn = rds_syndrome(word26);
assert!(
syn > 0,
"Data without checkword should have non-zero syndrome"
);
}
#[test]
fn test_rds_offset_for_syndrome_a() {
let result = rds_offset_for_syndrome(SYNDROME_A);
assert_eq!(result, Some('A'), "SYNDROME_A should return 'A'");
}
#[test]
fn test_rds_offset_for_syndrome_b() {
let result = rds_offset_for_syndrome(SYNDROME_B);
assert_eq!(result, Some('B'), "SYNDROME_B should return 'B'");
}
#[test]
fn test_rds_offset_for_syndrome_c() {
let result = rds_offset_for_syndrome(SYNDROME_C);
assert_eq!(result, Some('C'), "SYNDROME_C should return 'C'");
}
#[test]
fn test_rds_offset_for_syndrome_c_prime() {
let result = rds_offset_for_syndrome(SYNDROME_C_PRIME);
assert_eq!(result, Some('c'), "SYNDROME_C_PRIME should return 'c'");
}
#[test]
fn test_rds_offset_for_syndrome_d() {
let result = rds_offset_for_syndrome(SYNDROME_D);
assert_eq!(result, Some('D'), "SYNDROME_D should return 'D'");
}
#[test]
fn test_rds_offset_for_syndrome_invalid() {
let result = rds_offset_for_syndrome(0x123);
assert_eq!(result, None, "Invalid syndrome should return None");
}
#[test]
fn test_rds_data_from_word() {
let data = 0x1234u16;
let word26 = (data as u32) << 10 | 0x3FF; let extracted = rds_data_from_word(word26);
assert_eq!(extracted, data, "Should extract upper 16 bits as data");
}
#[test]
fn test_rds_data_from_word_zero() {
let word26 = 0x000003FFu32; let extracted = rds_data_from_word(word26);
assert_eq!(extracted, 0, "Should extract zero data correctly");
}
#[test]
fn test_rds_data_from_word_all_ones() {
let word26 = 0x03FFFFFFu32; let extracted = rds_data_from_word(word26);
assert_eq!(extracted, 0xFFFF, "Should extract all-ones data correctly");
}
#[test]
fn test_rds_parser_new() {
let parser = RdsParser::new();
assert_eq!(parser.shift, 0);
assert_eq!(parser.shift_len, 0);
assert_eq!(parser.ps, [b' '; 8]);
assert_eq!(parser.ps_received_mask, 0);
assert!(!parser.is_synced);
}
#[test]
fn test_rds_parser_verbose_mode() {
let mut parser = RdsParser::new();
assert!(!parser.verbose, "Verbose should be false by default");
parser.set_verbose(true);
assert!(
parser.verbose,
"Verbose should be true after set_verbose(true)"
);
parser.set_verbose(false);
assert!(
!parser.verbose,
"Verbose should be false after set_verbose(false)"
);
}
#[test]
fn test_rds_parser_station_name_empty() {
let parser = RdsParser::new();
assert_eq!(
parser.station_name(),
None,
"Station name should be None initially"
);
}
#[test]
fn test_rds_parser_radio_text_empty() {
let parser = RdsParser::new();
assert_eq!(
parser.radio_text(),
None,
"Radio text should be None initially"
);
}
#[test]
fn test_rds_parser_program_type() {
let parser = RdsParser::new();
assert_eq!(
parser.station_info().program_type,
0,
"Program type should be 0 initially"
);
assert_eq!(
parser.program_type_name(),
"None",
"Program type name should be 'None'"
);
}
#[test]
fn test_rds_parser_push_bits_insufficient() {
let mut parser = RdsParser::new();
let bits: Vec<u8> = vec![1, 0, 1, 0, 1]; parser.push_bits(&bits);
let (_, blocks, _) = parser.stats();
assert_eq!(blocks, 0, "Should not detect blocks with <26 bits");
}
#[test]
fn test_rds_parser_push_bits_invalid_block() {
let mut parser = RdsParser::new();
let bits: Vec<u8> = vec![0; 26]; parser.push_bits(&bits);
let (_, blocks, _) = parser.stats();
assert_eq!(blocks, 0, "Random bits should not produce valid blocks");
}
#[test]
fn test_rds_parser_rt_needs_multiple_segments() {
let mut parser = RdsParser::new();
parser.rt_received_mask[0] = true;
assert!(
parser.radio_text().is_none(),
"1 segment should not be enough for RT"
);
parser.rt_received_mask[1] = true;
parser.rt[0] = b'H';
parser.rt[1] = b'I';
assert!(
parser.radio_text().is_some(),
"2 segments should be enough for RT"
);
}
#[test]
fn test_rds_pty_names() {
assert_eq!(PTY_NAMES[0], "None");
assert_eq!(PTY_NAMES[1], "News");
assert_eq!(PTY_NAMES[11], "Classical");
}
#[test]
fn test_rds_di_flags_names() {
assert_eq!(DIFlags::flag_name(0), "dynamic_pty");
assert_eq!(DIFlags::flag_name(1), "compressed");
assert_eq!(DIFlags::flag_name(2), "artificial_head");
assert_eq!(DIFlags::flag_name(3), "stereo");
assert_eq!(DIFlags::flag_name(4), "unknown");
}
#[test]
fn test_rds_di_flags_set_get() {
let mut di = DIFlags::default();
assert!(!di.stereo);
di.set_flag(3, true); assert!(di.get_flag(3));
assert!(di.stereo);
di.set_flag(0, true); assert!(di.dynamic_pty);
}
#[test]
fn test_rds_di_flags_as_string() {
let di = DIFlags {
stereo: true,
..Default::default()
};
let s = di.as_string();
assert!(s.contains("stereo=true"));
assert!(s.contains("dynamic_pty=false"));
}
#[test]
fn test_rds_slc_variant_names() {
assert_eq!(RdsParser::slc_variant_name(0), "ECC and PI");
assert_eq!(RdsParser::slc_variant_name(3), "Language");
assert_eq!(RdsParser::slc_variant_name(255), "Unknown");
}
fn create_rds_block(data: u16, target_syndrome: u16) -> u32 {
let data_part = (data as u32) << 10;
let data_syndrome = rds_syndrome(data_part);
let needed_checkword_syndrome = data_syndrome ^ target_syndrome;
for checkword in 0u32..1024 {
if rds_syndrome(checkword) == needed_checkword_syndrome {
return data_part | checkword;
}
}
panic!(
"Could not find valid checkword for syndrome 0x{:03X}",
target_syndrome
);
}
fn word_to_bits(word: u32) -> Vec<u8> {
(0..26).rev().map(|i| ((word >> i) & 1) as u8).collect()
}
#[test]
fn test_create_and_decode_valid_block_a() {
let mut parser = RdsParser::new();
let data = 0x1234u16;
let block = create_rds_block(data, SYNDROME_A);
let bits = word_to_bits(block);
parser.push_bits(&bits);
let (_, blocks, _) = parser.stats();
assert_eq!(blocks, 1, "Should have detected one valid block");
assert!(
!parser.is_synced,
"Should not be synced after just one block"
);
}
#[test]
fn test_create_and_decode_valid_block_b() {
let mut parser = RdsParser::new();
let data = 0x5678u16;
let block = create_rds_block(data, SYNDROME_B);
let bits = word_to_bits(block);
parser.push_bits(&bits);
let (_, blocks, _) = parser.stats();
assert_eq!(blocks, 1, "Should have detected one valid block");
}
#[test]
fn test_rds_parser_debug_format() {
let mut parser = RdsParser::new();
parser.ps = *b"TESTFM ";
let debug_str = format!("{:?}", parser);
assert!(
debug_str.contains("TESTFM"),
"Debug format should show PS name"
);
}
#[test]
fn test_rds_sync_acquisition_with_full_group() {
let mut parser = RdsParser::new();
let block_a = create_rds_block(0xF212, SYNDROME_A); let block_b = create_rds_block(0x0408, SYNDROME_B); let block_c = create_rds_block(0xE20E, SYNDROME_C); let block_d = create_rds_block(0x2020, SYNDROME_D);
let block_a2 = create_rds_block(0xF212, SYNDROME_A);
let block_b2 = create_rds_block(0x0408, SYNDROME_B);
let block_c2 = create_rds_block(0xE20E, SYNDROME_C);
let block_d2 = create_rds_block(0x2020, SYNDROME_D);
let mut all_bits = Vec::new();
all_bits.extend(word_to_bits(block_a));
all_bits.extend(word_to_bits(block_b));
all_bits.extend(word_to_bits(block_c));
all_bits.extend(word_to_bits(block_d));
all_bits.extend(word_to_bits(block_a2));
all_bits.extend(word_to_bits(block_b2));
all_bits.extend(word_to_bits(block_c2));
all_bits.extend(word_to_bits(block_d2));
parser.push_bits(&all_bits);
let (_, blocks, groups) = parser.stats();
assert!(
blocks >= 6,
"Should have detected at least 6 blocks, got {}",
blocks
);
assert!(
parser.is_synced,
"Should have acquired sync after A,B,C,D sequence"
);
assert!(
groups >= 1,
"Should have decoded at least 1 group, got {}",
groups
);
}
#[test]
fn test_rds_parser_default() {
let parser = RdsParser::default();
assert_eq!(parser.shift, 0);
assert_eq!(parser.shift_len, 0);
}
#[test]
fn test_rds_parser_station_name_setter() {
let mut parser = RdsParser::new();
parser.update_ps(0, b'A', b'B');
parser.update_ps(1, b'C', b'D');
parser.update_ps(2, b'E', b'F');
parser.update_ps(3, b'G', b'H');
let ps = parser.station_name();
assert!(
ps.is_some(),
"PS should be Some after all 4 segments received sequentially"
);
assert_eq!(ps.unwrap(), "ABCDEFGH");
}
#[test]
fn test_rds_parser_radiotext_assembly() {
let mut parser = RdsParser::new();
parser.rt[0] = b'H';
parser.rt[1] = b'E';
parser.rt[2] = b'L';
parser.rt[3] = b'L';
parser.rt_received_mask[0] = true;
parser.rt[4] = b'O';
parser.rt[5] = b' ';
parser.rt[6] = b'W';
parser.rt[7] = b'O';
parser.rt_received_mask[1] = true;
let rt = parser.radio_text();
assert!(rt.is_some());
assert!(rt.unwrap().starts_with("HELLO WO"));
}
#[test]
fn test_rds_parser_di_flags_extraction() {
let mut di = DIFlags::default();
di.set_flag(0, true);
assert!(di.dynamic_pty);
assert!(!di.compressed);
assert!(!di.artificial_head);
assert!(!di.stereo);
di.set_flag(3, true);
assert!(di.stereo);
}
#[test]
fn test_rds_real_station_99_1_rfm() {
let mut parser = RdsParser::new();
parser.set_verbose(false);
let datas: [u16; 4] = [0xF212, 0x0408, 0xE20E, 0x2020];
parser.handle_group(datas, [true, true, true, true]);
assert!(parser.station_info.is_traffic_program, "TP should be true");
assert!(
!parser.station_info.is_traffic_announcement,
"TA should be false"
);
assert_eq!(parser.station_info.program_type, 0, "PTY should be 0");
assert!(parser.station_info.is_music, "Music flag should be true");
assert_eq!(parser.ps[0], 0x20, "PS char 0 should be space");
assert_eq!(parser.ps[1], 0x20, "PS char 1 should be space");
assert_eq!(
parser.ps_received_mask & 1,
1,
"PS segment 0 should be received"
);
}
#[test]
fn test_rds_comprehensive_real_station_sequence() {
let mut parser = RdsParser::new();
let group0: [u16; 4] = [0xF212, 0x0408, 0xE20E, 0x2020]; parser.handle_group(group0, [true, true, true, true]);
let group1: [u16; 4] = [0xF212, 0x0409, 0xE20E, 0x5246]; parser.handle_group(group1, [true, true, true, true]);
let group2: [u16; 4] = [0xF212, 0x040A, 0xE20E, 0x4D20]; parser.handle_group(group2, [true, true, true, true]);
let group3: [u16; 4] = [0xF212, 0x040B, 0xE20E, 0x2020]; parser.handle_group(group3, [true, true, true, true]);
let ps = parser.station_name();
assert!(ps.is_some(), "PS should be available after all segments");
let ps_str = ps.unwrap();
assert!(
ps_str.contains("RFM"),
"PS should contain 'RFM', got: '{}'",
ps_str
);
}
#[test]
fn test_rds_parser_stats() {
let mut parser = RdsParser::new();
let (bits, blocks, groups) = parser.stats();
assert_eq!(bits, 0);
assert_eq!(blocks, 0);
assert_eq!(groups, 0);
parser.push_bits(&[0, 1, 0, 1]);
let (bits, _, _) = parser.stats();
assert_eq!(bits, 4);
}
#[test]
fn test_rds_parser_has_data() {
let mut parser = RdsParser::new();
assert!(!parser.has_data(), "Should have no data initially");
parser.groups_decoded = 1;
assert!(parser.has_data(), "Should have data after group decoded");
}