//! `h5rs` on URLs (feature `remote`): every subcommand prints for //! `http://…/file.h5` what it prints for the local file (the name aside). //! The files are served by the range-request test server of //! clawhdf5-remote on 127.0.0.1. #![cfg(feature = "remote")] #[path = "../../clawhdf5-remote/tests/common/server.rs"] mod server; use std::path::{Path, PathBuf}; use std::process::Command; fn h5rs(args: &[&str]) -> (String, i32) { let out = Command::new(env!("CARGO_BIN_EXE_h5rs")) .args(args) .output() .expect("run h5rs"); let text = format!( "{}{}", String::from_utf8_lossy(&out.stdout), String::from_utf8_lossy(&out.stderr) ); (text, out.status.code().unwrap_or(-1)) } fn fixtures() -> Vec { let root = Path::new(env!("CARGO_MANIFEST_DIR")); [ "../clawhdf5/tests/fixtures/tall.h5", "../clawhdf5/tests/fixtures/written_by_v2_7_0.h5", "../clawhdf5/tests/fixtures/written_by_v2_7_0_paged.h5", "../clawhdf5/tests/fixtures/h5clear_mdc_image.h5", "../clawhdf5-format/tests/fixtures/fractal_heap_multiblock.h5", "../clawhdf5-format/tests/fixtures/legacy/tcompound.h5", "../clawhdf5-format/tests/fixtures/legacy/h5ex_g_iterate.h5", ] .iter() .map(|p| root.join(p)) .collect() } #[test] fn every_subcommand_reads_a_url_like_the_local_file() { let files = fixtures(); let served: Vec<(String, Vec)> = files .iter() .enumerate() .map(|(i, p)| { let name = p.file_name().unwrap().to_str().unwrap(); (format!("/{i}/{name}"), std::fs::read(p).unwrap()) }) .collect(); let server = server::Server::start(served.clone()); for (p, (url_path, _)) in files.iter().zip(&served) { let url = server.url(url_path); let local = p.to_str().unwrap(); for cmd in [ &["ls", "-r", "-v"][..], &["dump"], &["dump", "--json"], &["stat"], &["check", "--data"], ] { fn args<'a>(cmd: &[&'a str], f: &'a str) -> Vec<&'a str> { cmd.iter().copied().chain([f]).collect() } let (want, want_rc) = h5rs(&args(cmd, local)); let (got, got_rc) = h5rs(&args(cmd, &url)); assert_eq!( got.replace(&url, local), want, "h5rs {} {url}", cmd.join(" ") ); assert_eq!(got_rc, want_rc, "h5rs {} {url}", cmd.join(" ")); } let (a, rc) = h5rs(&["diff", local, &url]); assert_eq!(rc, 0, "h5rs diff {local} {url}: {a}"); } } #[test] fn url_errors_are_clean() { let server = server::Server::start(vec![("/x.h5".into(), vec![1u8; 100])]); let (out, rc) = h5rs(&["ls", &server.url("/missing.h5")]); assert_eq!(rc, 2, "{out}"); assert!(out.contains("404"), "{out}"); let (out, rc) = h5rs(&["ls", &server.url("/x.h5")]); assert_eq!(rc, 2, "{out}"); assert!(out.contains("not an HDF5 file"), "{out}"); let (out, rc) = h5rs(&["check", &server.url("/missing.h5")]); assert_eq!(rc, 2, "{out}"); #[cfg(not(feature = "remote-https"))] { let (out, rc) = h5rs(&["ls", "https://example.com/a.h5"]); assert_eq!(rc, 2, "{out}"); assert!(out.contains("`https` feature"), "{out}"); } } /// `check` downloads a remote file whole, but never trusts the length the /// server claims: a server claiming 2^62 bytes for a small file is refused /// before anything is allocated (it used to abort the process), and /// `--max-download` caps real files too. #[test] fn check_refuses_a_remote_file_beyond_the_download_limit() { use std::sync::atomic::Ordering; let tall = Path::new(env!("CARGO_MANIFEST_DIR")).join("../clawhdf5/tests/fixtures/tall.h5"); let server = server::Server::start(vec![("/t.h5".into(), std::fs::read(&tall).unwrap())]); let url = server.url("/t.h5"); let (out, rc) = h5rs(&["check", &url]); assert_eq!(rc, 0, "{out}"); let (out, rc) = h5rs(&["check", "--max-download", "1000", &url]); assert_eq!(rc, 2, "{out}"); assert!(out.contains("download limit of 1000 bytes"), "{out}"); server.shared.fake_total.store(1 << 62, Ordering::SeqCst); let (out, rc) = h5rs(&["check", &url]); assert_eq!(rc, 2, "{out}"); assert!(out.contains("more than the download limit"), "{out}"); }