use fst_reader::*;
use std::io::{BufRead, Seek};
use std::path::{Path, PathBuf};
mod utils;
use utils::hierarchy_to_str;
fn run_load_test(filename: &str, _filter: &FstFilter) {
let f = std::fs::File::open(filename).unwrap_or_else(|_| panic!("Failed to open {}", filename));
let mut reader = FstReader::open(std::io::BufReader::new(f)).unwrap();
load_header(&mut reader);
}
fn load_header<R: BufRead + Seek>(reader: &mut FstReader<R>) -> Vec<String> {
let mut is_real = Vec::new();
let mut hierarchy = Vec::new();
let foo = |entry: FstHierarchyEntry| {
match &entry {
FstHierarchyEntry::Var { tpe, handle, .. } => {
let is_var_real = match tpe {
FstVarType::Real
| FstVarType::RealParameter
| FstVarType::RealTime
| FstVarType::ShortReal => true,
_ => false,
};
let idx = handle.get_index();
if is_real.len() <= idx {
is_real.resize(idx + 1, false);
}
is_real[idx] = is_var_real;
}
_ => {}
};
let actual = hierarchy_to_str(&entry);
hierarchy.push(actual);
};
reader.read_hierarchy(foo).unwrap();
hierarchy
}
#[test]
#[ignore] fn load_sigrok() {
run_load_test("fsts/sigrok/libsigrok.vcd.fst", &FstFilter::all());
}
#[test]
fn load_verilator_incomplete() {
let f = std::fs::File::open("fsts/verilator/verilator-incomplete.fst")
.unwrap_or_else(|_| panic!("Failed to open file"));
let result = FstReader::open(std::io::BufReader::new(f));
assert!(matches!(result, Err(ReaderError::MissingGeometry())));
let f = std::fs::File::open("fsts/verilator/verilator-incomplete.fst")
.unwrap_or_else(|_| panic!("Failed to open file"));
let h = std::fs::File::open("fsts/verilator/verilator-incomplete.fst.hier")
.unwrap_or_else(|_| panic!("Failed to open file"));
let mut reader =
FstReader::open_incomplete(std::io::BufReader::new(f), std::io::BufReader::new(h)).unwrap();
load_header(&mut reader);
}
#[test]
fn load_time_table_treadle_gcd() {
let filename = "fsts/treadle/GCD.vcd.fst";
let f = std::fs::File::open(filename).unwrap_or_else(|_| panic!("Failed to open {}", filename));
let reader = FstReader::open_and_read_time_table(std::io::BufReader::new(f)).unwrap();
let expected = [0u64, 1, 2, 3, 4];
assert_eq!(reader.get_time_table().unwrap(), expected);
}
fn find_fst_files(dir: &Path) -> Vec<PathBuf> {
let mut out = Vec::new();
for entry in std::fs::read_dir(dir).unwrap().filter_map(Result::ok) {
let entry_path = entry.path();
if entry_path.is_dir() {
let mut sub = find_fst_files(&entry_path);
out.append(&mut sub);
}
if entry_path.to_str().unwrap().ends_with(".fst") {
out.push(entry_path);
}
}
out.sort();
out
}
#[test]
fn test_is_fst_file() {
let fsts = find_fst_files(Path::new("fsts/"));
for filename in fsts {
let mut f = std::fs::File::open(filename.clone())
.unwrap_or_else(|_| panic!("Failed to open {:?}", filename));
let is_fst = is_fst_file(&mut f);
let should_be_fst = true;
assert_eq!(
is_fst, should_be_fst,
"{filename:?} should be detected as a FST! ({should_be_fst})"
);
}
}
#[test]
fn load_long_hierarchy_name() {
let f =
std::fs::File::open("fsts/long_name.fst").unwrap_or_else(|_| panic!("Failed to open file"));
let mut result = FstReader::open(std::io::BufReader::new(f)).unwrap();
load_header(&mut result);
}
#[test]
fn test_multi_section_end_time_2sections() {
let fst = std::fs::File::open("fsts/partial/minimal_2sections.fst").unwrap();
let hier = std::fs::File::open("fsts/partial/minimal_2sections.fst.hier").unwrap();
let mut reader =
FstReader::open_incomplete(std::io::BufReader::new(fst), std::io::BufReader::new(hier))
.unwrap();
assert_eq!(reader.get_header().end_time, 200);
let mut value_count = 0;
reader
.read_signals(&FstFilter::all(), |_, _, _| value_count += 1)
.unwrap();
assert_eq!(value_count, 63); }
#[test]
fn test_multi_section_end_time_3sections() {
let fst = std::fs::File::open("fsts/partial/minimal_3sections.fst").unwrap();
let hier = std::fs::File::open("fsts/partial/minimal_3sections.fst.hier").unwrap();
let mut reader =
FstReader::open_incomplete(std::io::BufReader::new(fst), std::io::BufReader::new(hier))
.unwrap();
assert_eq!(reader.get_header().end_time, 300);
let mut value_count = 0;
reader
.read_signals(&FstFilter::all(), |_, _, _| value_count += 1)
.unwrap();
assert_eq!(value_count, 93); }
#[test]
fn test_truncated_3sections_reads_2_blocks() {
let fst = std::fs::File::open("fsts/partial/truncated_3sections.fst").unwrap();
let hier = std::fs::File::open("fsts/partial/truncated_3sections.fst.hier").unwrap();
let mut reader =
FstReader::open_incomplete(std::io::BufReader::new(fst), std::io::BufReader::new(hier))
.unwrap();
assert_eq!(reader.get_header().end_time, 300);
let mut value_count = 0;
let result = reader.read_signals(&FstFilter::all(), |_, _, _| value_count += 1);
assert!(
matches!(&result, Err(ReaderError::Io(e)) if e.kind() == std::io::ErrorKind::UnexpectedEof),
"Expected UnexpectedEof from truncated block, got: {:?}",
result
);
assert_eq!(value_count, 63);
}
#[test]
fn test_incomplete_real_signal_type() {
let fst = std::fs::File::open("fsts/partial/real_signal.fst").unwrap();
let hier = std::fs::File::open("fsts/partial/real_signal.fst.hier").unwrap();
let mut reader =
FstReader::open_incomplete(std::io::BufReader::new(fst), std::io::BufReader::new(hier))
.unwrap();
reader.read_hierarchy(|_| {}).unwrap();
let mut real_count = 0;
let mut string_count = 0;
reader
.read_signals(&FstFilter::all(), |_, _, value| match value {
FstSignalValue::Real(_) => real_count += 1,
FstSignalValue::String(_) => string_count += 1,
})
.unwrap();
assert_eq!(real_count, 10); assert_eq!(string_count, 10); }