mirror of
https://github.com/fish-shell/fish-shell.git
synced 2025-02-03 16:41:46 +08:00
7b7d16da48
This was based on a misunderstanding. On musl, 64-bit time_t on 32-bit architectures was introduced in version 1.2.0, by introducing new symbols. The old symbols still exist, to allow programs compiled against older versions to keep running on 1.2.0+, preserving ABI-compatibility. (see musl commit 38143339646a4ccce8afe298c34467767c899f51) Programs compiled against 1.2.0+ will get the new symbols, and will therefore think time_t is 64-bit. Unfortunately, rust's libc crate uses its own definition of these types, and does not check for musl version. Currently, it includes the pre-1.2.0 32-bit type. That means: - If you run on a 32-bit system like i686 - ... and compile against a C-library other than libc - ... and pass it a time_t-containing struct like timespec or stat ... you need to arrange for that library to be built against musl <1.2.0. Or, as https://github.com/ericonr/rust-time64 says: > Therefore, for "old" 32-bit targets (riscv32 is supposed to default to time64), > any Rust code that interacts with C code built on musl after 1.2.0, > using types based on time_t (arguably, the main ones are struct timespec and struct stat) in their interface, > will be completely miscompiled. However, while fish runs on i686 and compiles against pcre2, we do not pass pcre2 a time_t. Our only uses of time_t are confined to interactions with libc, in which case with musl we would simply use the legacy ABI. I have compiled an i686 fish against musl to confirm and can find no issue. This reverts commit55196ee2a0
. This reverts commit4992f88966
. This reverts commit46c8ba2c9f
. This reverts commit3a9b4149da
. This reverts commit5f9e9cbe74
. This reverts commit338579b78c
. This reverts commitd19e5508d7
. This reverts commitb64045dc18
. Closes #10634
193 lines
5.4 KiB
Rust
193 lines
5.4 KiB
Rust
use crate::env::{EnvMode, EnvStack, EnvVar, EnvVarFlags, Environment};
|
|
use crate::tests::prelude::*;
|
|
use crate::wchar::prelude::*;
|
|
use crate::wutil::wgetcwd;
|
|
use std::collections::HashMap;
|
|
use std::time::{SystemTime, UNIX_EPOCH};
|
|
|
|
/// An environment built around an std::map.
|
|
#[derive(Clone, Default)]
|
|
pub struct TestEnvironment {
|
|
pub vars: HashMap<WString, WString>,
|
|
}
|
|
impl TestEnvironment {
|
|
#[allow(dead_code)]
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
}
|
|
impl Environment for TestEnvironment {
|
|
fn getf(&self, name: &wstr, _mode: EnvMode) -> Option<EnvVar> {
|
|
self.vars
|
|
.get(name)
|
|
.map(|value| EnvVar::new(value.clone(), EnvVarFlags::default()))
|
|
}
|
|
fn get_names(&self, _flags: EnvMode) -> Vec<WString> {
|
|
self.vars.keys().cloned().collect()
|
|
}
|
|
}
|
|
|
|
/// A test environment that knows about PWD.
|
|
#[derive(Default)]
|
|
pub struct PwdEnvironment {
|
|
pub parent: TestEnvironment,
|
|
}
|
|
impl PwdEnvironment {
|
|
#[allow(dead_code)]
|
|
pub fn new() -> Self {
|
|
Self::default()
|
|
}
|
|
}
|
|
impl Environment for PwdEnvironment {
|
|
fn getf(&self, name: &wstr, mode: EnvMode) -> Option<EnvVar> {
|
|
if name == "PWD" {
|
|
return Some(EnvVar::new(wgetcwd(), EnvVarFlags::default()));
|
|
}
|
|
self.parent.getf(name, mode)
|
|
}
|
|
|
|
fn get_names(&self, flags: EnvMode) -> Vec<WString> {
|
|
let mut res = self.parent.get_names(flags);
|
|
if !res.iter().any(|n| n == "PWD") {
|
|
res.push(L!("PWD").to_owned());
|
|
}
|
|
res
|
|
}
|
|
}
|
|
|
|
/// Helper for test_timezone_env_vars().
|
|
fn return_timezone_hour(tstamp: SystemTime, timezone: &wstr) -> libc::c_int {
|
|
let vars = EnvStack::globals().create_child(true /* dispatches_var_changes */);
|
|
|
|
vars.set_one(L!("TZ"), EnvMode::EXPORT, timezone.to_owned());
|
|
|
|
let _var = vars.get(L!("TZ"));
|
|
|
|
let tstamp: libc::time_t = tstamp
|
|
.duration_since(UNIX_EPOCH)
|
|
.unwrap()
|
|
.as_secs()
|
|
.try_into()
|
|
.unwrap();
|
|
let mut local_time: libc::tm = unsafe { std::mem::zeroed() };
|
|
unsafe { libc::localtime_r(&tstamp, &mut local_time) };
|
|
|
|
local_time.tm_hour
|
|
}
|
|
|
|
/// Verify that setting TZ calls tzset() in the current shell process.
|
|
fn test_timezone_env_vars() {
|
|
// Confirm changing the timezone affects fish's idea of the local time.
|
|
let tstamp = SystemTime::now();
|
|
|
|
let first_tstamp = return_timezone_hour(tstamp, L!("UTC-1"));
|
|
let second_tstamp = return_timezone_hour(tstamp, L!("UTC-2"));
|
|
let delta = second_tstamp - first_tstamp;
|
|
assert!(delta == 1 || delta == -23);
|
|
}
|
|
|
|
// Verify that setting special env vars have the expected effect on the current shell process.
|
|
#[test]
|
|
#[serial]
|
|
fn test_env_vars() {
|
|
let _cleanup = test_init();
|
|
test_timezone_env_vars();
|
|
// TODO: Add tests for the locale and ncurses vars.
|
|
|
|
let v1 = EnvVar::new(L!("abc").to_owned(), EnvVarFlags::EXPORT);
|
|
let v2 = EnvVar::new_vec(vec![L!("abc").to_owned()], EnvVarFlags::EXPORT);
|
|
let v3 = EnvVar::new_vec(vec![L!("abc").to_owned()], EnvVarFlags::empty());
|
|
let v4 = EnvVar::new_vec(
|
|
vec![L!("abc").to_owned(), L!("def").to_owned()],
|
|
EnvVarFlags::EXPORT,
|
|
);
|
|
assert_eq!(v1, v2);
|
|
assert_ne!(v1, v3);
|
|
assert_ne!(v1, v4);
|
|
}
|
|
|
|
#[test]
|
|
#[serial]
|
|
fn test_env_snapshot() {
|
|
let _cleanup = test_init();
|
|
std::fs::create_dir_all("test/fish_env_snapshot_test/").unwrap();
|
|
let parser = TestParser::new();
|
|
let vars = parser.vars();
|
|
parser.pushd("test/fish_env_snapshot_test/");
|
|
vars.push(true);
|
|
let before_pwd = vars.get(L!("PWD")).unwrap().as_string();
|
|
vars.set_one(
|
|
L!("test_env_snapshot_var"),
|
|
EnvMode::default(),
|
|
L!("before").to_owned(),
|
|
);
|
|
let snapshot = vars.snapshot();
|
|
vars.set_one(L!("PWD"), EnvMode::default(), L!("/newdir").to_owned());
|
|
vars.set_one(
|
|
L!("test_env_snapshot_var"),
|
|
EnvMode::default(),
|
|
L!("after").to_owned(),
|
|
);
|
|
vars.set_one(
|
|
L!("test_env_snapshot_var_2"),
|
|
EnvMode::default(),
|
|
L!("after").to_owned(),
|
|
);
|
|
|
|
// vars should be unaffected by the snapshot
|
|
assert_eq!(vars.get(L!("PWD")).unwrap().as_string(), L!("/newdir"));
|
|
assert_eq!(
|
|
vars.get(L!("test_env_snapshot_var")).unwrap().as_string(),
|
|
L!("after")
|
|
);
|
|
assert_eq!(
|
|
vars.get(L!("test_env_snapshot_var_2")).unwrap().as_string(),
|
|
L!("after")
|
|
);
|
|
|
|
// snapshot should have old values of vars
|
|
assert_eq!(snapshot.get(L!("PWD")).unwrap().as_string(), before_pwd);
|
|
assert_eq!(
|
|
snapshot
|
|
.get(L!("test_env_snapshot_var"))
|
|
.unwrap()
|
|
.as_string(),
|
|
L!("before")
|
|
);
|
|
assert_eq!(snapshot.get(L!("test_env_snapshot_var_2")), None);
|
|
|
|
// snapshots see global var changes except for perproc like PWD
|
|
vars.set_one(
|
|
L!("test_env_snapshot_var_3"),
|
|
EnvMode::GLOBAL,
|
|
L!("reallyglobal").to_owned(),
|
|
);
|
|
assert_eq!(
|
|
vars.get(L!("test_env_snapshot_var_3")).unwrap().as_string(),
|
|
L!("reallyglobal")
|
|
);
|
|
assert_eq!(
|
|
snapshot
|
|
.get(L!("test_env_snapshot_var_3"))
|
|
.unwrap()
|
|
.as_string(),
|
|
L!("reallyglobal")
|
|
);
|
|
|
|
vars.pop();
|
|
parser.popd();
|
|
}
|
|
|
|
// Can't push/pop from globals.
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_no_global_push() {
|
|
EnvStack::globals().push(true);
|
|
}
|
|
|
|
#[test]
|
|
#[should_panic]
|
|
fn test_no_global_pop() {
|
|
EnvStack::globals().pop();
|
|
}
|