use std::fs;
use std::path::{Path, PathBuf};
use std::process::Command;
use mf2::Host;
use mf2::host_std::ZONES_HOST;
const SUPPLIED: &str = "Test/Supplied";
const SUPPLIED_OFFSET: i32 = 2 * 3600 + 1800;
const REAL: &str = "Europe/Paris";
const WINTER_MS: i64 = 1_136_160_000_000;
const CHILD: &str = "MF2_ZONE_DB_CHILD";
#[test]
fn a_lookup_reads_the_database_this_build_asked_for() {
let dir = supplied_database();
let status = Command::new(std::env::current_exe().expect("the test binary"))
.args([
"--exact",
"in_a_process_whose_tzdir_is_supplied",
"--nocapture",
])
.env("TZDIR", &dir)
.env(CHILD, "1")
.status()
.expect("the test binary runs again");
fs::remove_dir_all(dir.parent().expect("the test's own directory")).ok();
assert!(
status.success(),
"the child's assertions failed: see its output above"
);
}
#[test]
fn in_a_process_whose_tzdir_is_supplied() {
if std::env::var_os(CHILD).is_none() {
return;
}
assert_eq!(ZONES_HOST.zone_offset("Mars/Olympus_Mons", 0), None);
#[cfg(not(feature = "tzdb-bundled"))]
{
assert_eq!(
ZONES_HOST.zone_offset(SUPPLIED, WINTER_MS),
Some(SUPPLIED_OFFSET),
"the system's database is the one in `TZDIR`"
);
assert_eq!(
ZONES_HOST.zone_offset(REAL, WINTER_MS),
None,
"a zone the machine's database does not hold is *Bad Option*"
);
}
#[cfg(feature = "tzdb-bundled")]
{
assert_eq!(
ZONES_HOST.zone_offset(REAL, WINTER_MS),
Some(3600),
"the bundle answers whatever `TZDIR` holds"
);
assert_eq!(
ZONES_HOST.zone_offset(SUPPLIED, WINTER_MS),
None,
"the bundle holds no zone of the test's own"
);
}
}
fn supplied_database() -> PathBuf {
let root = std::env::temp_dir().join(format!("mf2-zone-db-{}", std::process::id()));
let dir = root.join("zoneinfo");
let (region, city) = SUPPLIED.split_once('/').expect("a two-part zone name");
fs::create_dir_all(dir.join(region)).expect("the supplied database's directory");
write_tzif(&dir.join(region).join(city), SUPPLIED_OFFSET, "TST");
dir
}
fn write_tzif(path: &Path, offset: i32, abbreviation: &str) {
let mut chars = abbreviation.as_bytes().to_vec();
chars.push(0);
let mut bytes = Vec::new();
for _ in 0..2 {
bytes.extend_from_slice(b"TZif2");
bytes.extend_from_slice(&[0u8; 15]);
let charcnt = u32::try_from(chars.len()).expect("a short abbreviation");
for count in [0u32, 0, 0, 0, 1, charcnt] {
bytes.extend_from_slice(&count.to_be_bytes());
}
bytes.extend_from_slice(&offset.to_be_bytes()); bytes.push(0); bytes.push(0); bytes.extend_from_slice(&chars);
}
let hours = offset / 3600;
let minutes = (offset % 3600) / 60;
bytes.extend_from_slice(format!("\n{abbreviation}-{hours}:{minutes:02}\n").as_bytes());
fs::write(path, &bytes).expect("the supplied TZif file");
}