use std::path::Path;
fn fixture_path(subdir: &str, name: &str) -> Option<std::path::PathBuf> {
let base = Path::new(env!("CARGO_MANIFEST_DIR"))
.parent()
.unwrap()
.join("testdata")
.join(subdir);
let path = base.join(name);
if path.exists() {
Some(path)
} else {
None
}
}
macro_rules! skip_if_missing {
($subdir:expr, $name:expr) => {
match fixture_path($subdir, $name) {
Some(p) => p,
None => {
eprintln!("SKIPPED: fixture {}/{} not found", $subdir, $name);
return;
}
}
};
}
#[cfg(feature = "netcdf4")]
fn create_sparse_huge_nc4_fixture(path: &Path) {
const DIM: usize = 1 << 20;
let mut file = netcdf::create_with(path, netcdf::Options::NETCDF4).unwrap();
file.add_dimension("row", DIM).unwrap();
file.add_dimension("col", DIM).unwrap();
{
let mut variable = file.add_variable::<f32>("sparse", &["row", "col"]).unwrap();
variable.set_chunking(&[1024, 1024]).unwrap();
variable.set_fill_value(42.5_f32).unwrap();
}
file.enddef().unwrap();
}
#[test]
fn test_cdf1_simple() {
let path = skip_if_missing!("netcdf3", "cdf1_simple.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
assert_eq!(file.format(), netcdf_reader::NcFormat::Classic);
assert_eq!(file.dimensions().len(), 2);
assert_eq!(file.dimensions()[0].name, "x");
assert_eq!(file.dimensions()[0].size, 5);
let var = file.variable("temp").unwrap();
assert_eq!(var.shape(), vec![5, 10]);
let classic = file.as_classic().unwrap();
let data: ndarray::ArrayD<f32> = classic.read_variable("temp").unwrap();
assert_eq!(data.shape(), &[5, 10]);
assert!((data[[0, 0]] - 0.0).abs() < 1e-6);
}
#[test]
fn test_cdf5_new_types() {
let path = skip_if_missing!("netcdf3", "cdf5_new_types.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
assert_eq!(file.format(), netcdf_reader::NcFormat::Cdf5);
let classic = file.as_classic().unwrap();
let ubyte_data: ndarray::ArrayD<u8> = classic.read_variable("ubyte_var").unwrap();
assert_eq!(ubyte_data.as_slice().unwrap(), &[1, 2, 3, 4]);
let int64_data: ndarray::ArrayD<i64> = classic.read_variable("int64_var").unwrap();
assert_eq!(int64_data.as_slice().unwrap(), &[1, 2, 3, 4]);
}
#[test]
fn test_record_vars() {
let path = skip_if_missing!("netcdf3", "record_vars.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
let var = file.variable("series").unwrap();
assert!(var.dimensions()[0].is_unlimited);
assert_eq!(var.shape(), vec![3, 5]);
let classic = file.as_classic().unwrap();
let data: ndarray::ArrayD<f64> = classic.read_variable("series").unwrap();
assert_eq!(data[[0, 0]], 1.0);
assert_eq!(data[[2, 4]], 15.0);
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_basic() {
let path = skip_if_missing!("netcdf4", "nc4_basic.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
assert!(matches!(
file.format(),
netcdf_reader::NcFormat::Nc4 | netcdf_reader::NcFormat::Nc4Classic
));
let dims = file.dimensions();
assert!(dims.len() >= 2);
let vars = file.variables();
assert!(!vars.is_empty());
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_from_bytes_with_options() {
let path = skip_if_missing!("netcdf4", "nc4_basic.nc");
let bytes = std::fs::read(&path).unwrap();
let file = netcdf_reader::NcFile::from_bytes_with_options(
&bytes,
netcdf_reader::NcOpenOptions {
chunk_cache_bytes: 1024,
chunk_cache_slots: 17,
filter_registry: None,
},
)
.unwrap();
assert!(matches!(
file.format(),
netcdf_reader::NcFormat::Nc4 | netcdf_reader::NcFormat::Nc4Classic
));
let data: ndarray::ArrayD<f64> = file.read_variable("data").unwrap();
assert_eq!(data.shape(), &[5, 10]);
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_compressed() {
let path = skip_if_missing!("netcdf4", "nc4_compressed.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
assert!(matches!(
file.format(),
netcdf_reader::NcFormat::Nc4 | netcdf_reader::NcFormat::Nc4Classic
));
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_groups_nested_lookup_and_read() {
let path = skip_if_missing!("netcdf4", "nc4_groups.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
let obs = file.group("obs").unwrap();
assert_eq!(obs.name, "obs");
let temp_data: ndarray::ArrayD<f32> = file.read_variable("/obs/temperature").unwrap();
assert_eq!(temp_data.shape(), &[3]);
assert_eq!(obs.dimension("time").unwrap().size, 3);
let temperature = file.variable("obs/temperature").unwrap();
let dim_names: Vec<&str> = temperature
.dimensions()
.iter()
.map(|d| d.name.as_str())
.collect();
assert_eq!(dim_names, vec!["time"]);
assert!((temp_data[[1]] - 21.0).abs() < 1e-6);
assert!(file.variable("temperature").is_err());
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_classic_model() {
let path = skip_if_missing!("netcdf4", "nc4_classic_model.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
assert_eq!(file.format(), netcdf_reader::NcFormat::Nc4Classic);
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_same_size_dims() {
let path = skip_if_missing!("netcdf4", "same_size_dims.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
let var = file.variable("temperature").unwrap();
assert_eq!(var.dimensions().len(), 2);
let dim_names: Vec<&str> = var.dimensions().iter().map(|d| d.name.as_str()).collect();
assert_eq!(dim_names, vec!["lat", "lon"]);
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_string_variable_reads() {
let path = skip_if_missing!("netcdf4", "nc4_string_var.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
let strings = file.read_variable_as_strings("names").unwrap();
assert_eq!(strings, vec!["alpha", "beta", "gamma", "delta"]);
let err = file.read_variable_as_string("names").unwrap_err();
assert!(matches!(err, netcdf_reader::Error::InvalidData(_)));
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_read_variable_unified() {
let path = skip_if_missing!("netcdf4", "nc4_basic.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
let data: ndarray::ArrayD<f64> = file.read_variable("data").unwrap();
assert_eq!(data.shape(), &[5, 10]);
assert!((data[[0, 0]] - 0.0).abs() < 1e-10);
assert!((data[[4, 9]] - 49.0).abs() < 1e-10);
}
#[cfg(feature = "netcdf4")]
#[test]
fn test_nc4_sparse_huge_logical_slice_reads_fill_value() {
let temp_dir = tempfile::tempdir().unwrap();
let path = temp_dir.path().join("sparse_huge.nc");
create_sparse_huge_nc4_fixture(&path);
let expected_fill = netcdf::open(&path)
.unwrap()
.variable("sparse")
.unwrap()
.fill_value::<f32>()
.unwrap()
.unwrap_or(0.0);
let file = netcdf_reader::NcFile::open(&path).unwrap();
let selection = netcdf_reader::NcSliceInfo {
selections: vec![
netcdf_reader::NcSliceInfoElem::Index((1 << 20) - 1),
netcdf_reader::NcSliceInfoElem::Index((1 << 20) - 1),
],
};
let sliced: ndarray::ArrayD<f32> = file.read_variable_slice("sparse", &selection).unwrap();
assert_eq!(sliced.shape(), &[]);
assert_eq!(sliced[[]], expected_fill);
let hdf5 = hdf5_reader::Hdf5File::open(&path).unwrap();
let dataset = hdf5.dataset("/sparse").unwrap();
let hdf5_selection = hdf5_reader::SliceInfo {
selections: vec![
hdf5_reader::SliceInfoElem::Slice {
start: ((1 << 20) - 1) as u64,
end: 1 << 20,
step: 1,
},
hdf5_reader::SliceInfoElem::Slice {
start: ((1 << 20) - 1) as u64,
end: 1 << 20,
step: 1,
},
],
};
let hdf5_sliced: ndarray::ArrayD<f32> = dataset.read_slice(&hdf5_selection).unwrap();
assert_eq!(hdf5_sliced.shape(), &[1, 1]);
assert_eq!(hdf5_sliced[[0, 0]], expected_fill);
}
#[test]
fn test_classic_read_variable_unified() {
let path = skip_if_missing!("netcdf3", "cdf1_simple.nc");
let file = netcdf_reader::NcFile::open(&path).unwrap();
let data: ndarray::ArrayD<f32> = file.read_variable("temp").unwrap();
assert_eq!(data.shape(), &[5, 10]);
assert!((data[[0, 0]] - 0.0).abs() < 1e-6);
}