use std::process::Command;
fn ilo() -> Command {
Command::new(env!("CARGO_BIN_EXE_ilo"))
}
#[cfg(feature = "cranelift")]
const ENGINES: &[&str] = &["--vm", "--jit"];
#[cfg(not(feature = "cranelift"))]
const ENGINES: &[&str] = &["--vm"];
fn run(engine: &str, src: &str, args: &[&str]) -> (bool, String, String) {
let mut cmd = ilo();
cmd.arg(src).arg(engine);
for a in args {
cmd.arg(a);
}
let out = cmd.output().expect("failed to run ilo");
(
out.status.success(),
String::from_utf8_lossy(&out.stdout).trim().to_string(),
String::from_utf8_lossy(&out.stderr).trim().to_string(),
)
}
fn check_offset(engine: &str, tz: &str, epoch: i64, expected: i64) {
let src = r#"f tz:t n:n>R n t;tz-offset tz n"#;
let epoch_str = epoch.to_string();
let (ok, stdout, stderr) = run(engine, src, &["f", tz, &epoch_str]);
assert!(ok, "{engine}: tz-offset {tz:?} {epoch} failed: {stderr}");
let got: i64 = stdout
.parse()
.unwrap_or_else(|_| panic!("{engine}: expected number, got {stdout:?}"));
assert_eq!(
got, expected,
"{engine}: tz-offset {tz:?} at {epoch}: expected {expected}s, got {got}s"
);
}
#[test]
fn london_winter_is_utc_cross_engine() {
for engine in ENGINES {
check_offset(engine, "Europe/London", 1_704_110_400, 0);
}
}
#[test]
fn london_summer_is_bst_cross_engine() {
for engine in ENGINES {
check_offset(engine, "Europe/London", 1_719_835_200, 3600);
}
}
#[test]
fn tokyo_no_dst_cross_engine() {
for engine in ENGINES {
check_offset(engine, "Asia/Tokyo", 0, 32400);
check_offset(engine, "Asia/Tokyo", 1_719_835_200, 32400);
}
}
#[test]
fn nyc_winter_est_cross_engine() {
for engine in ENGINES {
check_offset(engine, "America/New_York", 1_705_320_000, -18000);
}
}
#[test]
fn nyc_summer_edt_cross_engine() {
for engine in ENGINES {
check_offset(engine, "America/New_York", 1_721_044_800, -14400);
}
}
#[test]
fn invalid_tz_returns_err_cross_engine() {
let src = r#"f tz:t n:n>R n t;tz-offset tz n"#;
for engine in ENGINES {
let (ok, stdout, stderr) = run(engine, src, &["f", "Not/A/Timezone", "0"]);
assert!(
!ok,
"{engine}: expected exit 1 for bad tz name, got success. stdout={stdout:?}"
);
assert!(
stderr.contains("tz-offset"),
"{engine}: expected 'tz-offset' in error message, got: {stderr:?}"
);
assert!(
stderr.contains("Not/A/Timezone"),
"{engine}: expected tz name in error message, got: {stderr:?}"
);
}
}
#[test]
fn tz_offset_bang_unwrap_cross_engine() {
let src = r#"f tz:t n:n>R n t;v=tz-offset! tz n;~v"#;
for engine in ENGINES {
let (ok, stdout, stderr) = run(engine, src, &["f", "Asia/Tokyo", "0"]);
assert!(ok, "{engine}: tz-offset! failed: {stderr}");
assert_eq!(
stdout, "32400",
"{engine}: tz-offset! expected 32400, got {stdout:?}"
);
}
}
#[test]
fn utc_zero_offset_cross_engine() {
for engine in ENGINES {
check_offset(engine, "UTC", 0, 0);
check_offset(engine, "UTC", 1_719_835_200, 0);
}
}
#[test]
fn london_year_boundary_gmt_cross_engine() {
for engine in ENGINES {
check_offset(engine, "Europe/London", 1_704_067_199, 0);
check_offset(engine, "Europe/London", 1_704_067_200, 0);
}
}
#[test]
fn london_dst_spring_forward_cross_engine() {
for engine in ENGINES {
check_offset(engine, "Europe/London", 1_711_846_799, 0);
check_offset(engine, "Europe/London", 1_711_846_800, 3600);
}
}
#[test]
fn london_dst_fall_back_cross_engine() {
for engine in ENGINES {
check_offset(engine, "Europe/London", 1_729_990_799, 3600);
check_offset(engine, "Europe/London", 1_729_990_800, 0);
}
}
fn run_verify_err(src: &str) -> String {
let out = ilo()
.arg(src)
.arg("--vm")
.output()
.expect("failed to run ilo");
assert!(
!out.status.success(),
"expected verify failure, got success. stdout={}",
String::from_utf8_lossy(&out.stdout)
);
String::from_utf8_lossy(&out.stderr).to_string()
}
#[test]
fn tz_offset_wrong_tz_arg_type_rejected() {
let stderr = run_verify_err(r#"f>R n t;tz-offset 1 0"#);
assert!(
stderr.contains("ILO-T013"),
"expected ILO-T013 for tz-offset non-text tz arg, got: {stderr}"
);
assert!(
stderr.contains("tz-offset"),
"expected message to mention tz-offset, got: {stderr}"
);
}
#[test]
fn tz_offset_wrong_epoch_arg_type_rejected() {
let stderr = run_verify_err(r#"f>R n t;tz-offset "UTC" "now""#);
assert!(
stderr.contains("ILO-T013"),
"expected ILO-T013 for tz-offset non-number epoch arg, got: {stderr}"
);
assert!(
stderr.contains("tz-offset"),
"expected message to mention tz-offset, got: {stderr}"
);
}