use temp_dir::TempDir;
use xdmf::TimeSeriesWriter;
#[test]
fn write_and_verify_binary() {
fn read_f64_le(path: &std::path::Path) -> Vec<f64> {
let bytes = std::fs::read(path).unwrap();
bytes
.as_chunks::<8>()
.0
.iter()
.map(|c| f64::from_le_bytes(*c))
.collect()
}
fn read_u32_le(path: &std::path::Path) -> Vec<u32> {
let bytes = std::fs::read(path).unwrap();
bytes
.as_chunks::<4>()
.0
.iter()
.map(|c| u32::from_le_bytes(*c))
.collect()
}
let coords = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0];
let connectivity = [0_u32, 1, 2, 0, 2, 3];
let cell_types = [xdmf::CellType::Triangle, xdmf::CellType::Triangle];
let tmp_dir = TempDir::new().unwrap();
let xdmf_file_path = tmp_dir.path().join("test_output");
let xdmf_writer = TimeSeriesWriter::new(&xdmf_file_path, xdmf::DataStorage::Binary).unwrap();
let mut xdmf_writer = xdmf_writer
.write_mesh(&coords, &connectivity, &cell_types)
.unwrap();
xdmf_writer
.write_time_step("0", |step| {
step.point_data(
"temperature",
xdmf::DataAttribute::Scalar,
vec![10.0, 11.0, 12.0, 13.0],
)?;
step.cell_data("region_id", xdmf::DataAttribute::Scalar, vec![100_u32, 200])
})
.unwrap();
let xdmf_file = xdmf_file_path.with_extension("xdmf2");
let xdmf_xml = std::fs::read_to_string(&xdmf_file).unwrap();
assert!(xdmf_xml.contains(r#"Format="Binary""#));
assert!(xdmf_xml.contains(r#"Endian="Little""#));
assert!(!xdmf_xml.contains('\\'), "paths must use forward slashes");
assert!(xdmf_xml.contains(r#"NumberType="UInt" Format="Binary" Precision="4""#));
assert!(xdmf_xml.contains(r#"NumberType="Float" Format="Binary" Precision="8""#));
let bin_dir = xdmf_file_path.with_extension("bin");
assert_eq!(read_f64_le(&bin_dir.join("points.bin")), coords.to_vec());
assert_eq!(
read_u32_le(&bin_dir.join("cells.bin")),
vec![0, 1, 2, 0, 2, 3]
);
assert_eq!(
read_f64_le(&bin_dir.join("data_t_0_0.bin")),
vec![10.0, 11.0, 12.0, 13.0]
);
assert_eq!(read_u32_le(&bin_dir.join("data_t_0_1.bin")), vec![100, 200]);
}
#[test]
fn binary_rejects_64_bit_integer_data() {
let coords = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0];
let connectivity = [0_u32, 1, 2];
let cell_types = [xdmf::CellType::Triangle];
let tmp_dir = TempDir::new().unwrap();
let xdmf_file_path = tmp_dir.path().join("test_output");
let xdmf_writer = TimeSeriesWriter::new(&xdmf_file_path, xdmf::DataStorage::Binary).unwrap();
let mut xdmf_writer = xdmf_writer
.write_mesh(&coords, &connectivity, &cell_types)
.unwrap();
for value in [i64::from(i32::MAX) + 1, 7_i64] {
let res = xdmf_writer.write_time_step("0", |step| {
step.cell_data("region_id", xdmf::DataAttribute::Scalar, vec![value])
});
std::assert_matches!(
res.unwrap_err(),
xdmf::Error::InvalidData { reason }
if reason.contains("cannot hold i64 data")
&& reason.contains("use another DataStorage"),
"Binary must refuse an i64 of {value}"
);
}
let res = xdmf_writer.write_time_step("0", |step| {
step.cell_data("region_id", xdmf::DataAttribute::Scalar, vec![7_u64])
});
std::assert_matches!(
res.unwrap_err(),
xdmf::Error::InvalidData { reason } if reason.contains("cannot hold u64 data")
);
let bin_dir = xdmf_file_path.with_extension("bin");
assert!(!bin_dir.join("data_t_0_0.bin").exists());
xdmf_writer
.write_time_step("1", |step| {
step.cell_data("region_id", xdmf::DataAttribute::Scalar, vec![7_u32])
})
.unwrap();
}
#[test]
fn write_and_verify_binary_signed_integers() {
fn read_i32_le(path: &std::path::Path) -> Vec<i32> {
let bytes = std::fs::read(path).unwrap();
bytes
.as_chunks::<4>()
.0
.iter()
.map(|c| i32::from_le_bytes(*c))
.collect()
}
let coords = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0];
let connectivity = [0_u32, 1, 2];
let cell_types = [xdmf::CellType::Triangle];
let tmp_dir = TempDir::new().unwrap();
let xdmf_file_path = tmp_dir.path().join("test_output");
let xdmf_writer = TimeSeriesWriter::new(&xdmf_file_path, xdmf::DataStorage::Binary).unwrap();
let mut xdmf_writer = xdmf_writer
.write_mesh(&coords, &connectivity, &cell_types)
.unwrap();
xdmf_writer
.write_time_step("0", |step| {
step.point_data("level_i32", xdmf::DataAttribute::Scalar, vec![-2_i32, 0, 2])?;
step.point_data(
"flag_u32",
xdmf::DataAttribute::Scalar,
vec![0_u32, 1, u32::MAX],
)
})
.unwrap();
let xdmf_xml = std::fs::read_to_string(xdmf_file_path.with_extension("xdmf2")).unwrap();
assert_eq!(
xdmf_xml
.matches(r#"NumberType="Int" Format="Binary" Precision="4""#)
.count(),
1
);
assert_eq!(
xdmf_xml
.matches(r#"NumberType="UInt" Format="Binary" Precision="4""#)
.count(),
2, );
let bin_dir = xdmf_file_path.with_extension("bin");
assert_eq!(read_i32_le(&bin_dir.join("data_t_0_0.bin")), vec![-2, 0, 2]);
assert_eq!(
std::fs::read(bin_dir.join("data_t_0_1.bin")).unwrap().len(),
12,
"3 values at 4 bytes each, with nothing widened or narrowed on the way out"
);
}
#[test]
fn write_and_verify_binary_f32() {
fn read_f32_le(path: &std::path::Path) -> Vec<f32> {
let bytes = std::fs::read(path).unwrap();
bytes
.as_chunks::<4>()
.0
.iter()
.map(|c| f32::from_le_bytes(*c))
.collect()
}
let coords: [f32; 12] = [0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 1.0, 0.0, 0.0, 1.0, 0.0];
let connectivity = [0_u32, 1, 2, 0, 2, 3];
let cell_types = [xdmf::CellType::Triangle, xdmf::CellType::Triangle];
let tmp_dir = TempDir::new().unwrap();
let xdmf_file_path = tmp_dir.path().join("test_output");
let xdmf_writer = TimeSeriesWriter::new(&xdmf_file_path, xdmf::DataStorage::Binary).unwrap();
let mut xdmf_writer = xdmf_writer
.write_mesh(&coords, &connectivity, &cell_types)
.unwrap();
xdmf_writer
.write_time_step("0", |step| {
step.point_data(
"temperature",
xdmf::DataAttribute::Scalar,
vec![10.5_f32, 11.5, 12.5, 13.5],
)
})
.unwrap();
let xdmf_xml = std::fs::read_to_string(xdmf_file_path.with_extension("xdmf2")).unwrap();
assert!(xdmf_xml.contains(r#"NumberType="Float" Format="Binary" Precision="4""#));
assert!(!xdmf_xml.contains(r#"NumberType="Float" Format="Binary" Precision="8""#));
let bin_dir = xdmf_file_path.with_extension("bin");
let points = bin_dir.join("points.bin");
assert_eq!(std::fs::metadata(&points).unwrap().len(), 12 * 4);
float_cmp::assert_approx_eq!(&[f32], &read_f32_le(&points), &coords);
float_cmp::assert_approx_eq!(
&[f32],
&read_f32_le(&bin_dir.join("data_t_0_0.bin")),
&[10.5, 11.5, 12.5, 13.5]
);
}