"cargo test" captures stdout by default but not stderr.
So it's probably still useful to suppress test output like
in function 'recursive1'
in function 'recursive2'
[repeats many times]
This was done by should_suppress_stderr_for_tests() which has been
broken. Fix that, but only for the relevant cases instead of setting
a global.
On some platforms, Rust's std::env::current_exe() may use relative
paths from at least argv[0]. That function also canonicalizes the
path, so we could only detect this case by duplicating logic from
std::env::current_exe() (not sure if that's worth it).
Relative path canonicalization seems potentially surprising, especially
after fish has used "cd". Let's try to reduce surprise by saving the
value we compute at startup (before any "cd"), and use only that.
The remaining problem is that
creat("/some/directory/with/FISH");
chdir("/some/directory/with/");
execve("/bin/fish", "FISH", "-c", "status fish-path")
surprisingly prints "/some/directory/with/FISH" on some platforms.
But that requires the user actively trying to break things (passing
a relative argv[0] that doesn't match the cwd).
When "status fish-path" fails, we fall back to argv[0],
hoping that it contains an absolute path to fish, or at
least is equal to "fish" (which can happen on OpenBSD, see
https://github.com/rust-lang/rust/issues/60560#issuecomment-489425888).
I don't think it makes sense to duplicate logic that's probably
already in std::env::current_exe().
Since c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18), a
change in locale variables can no longer cause us to toggle between
"…" or "...". Have the control flow reflect this.
We use the following locale-variables via locale-aware C functions
(to look them up, grep for "libc::$function_name"):
- LC_CTYPE: wcwidth
- LC_MESSAGES: strerror, strerror
- LC_NUMERIC: localeconv/localeconv_l, snprintf (only in tests)
- LC_TIME: strftime
Additionally, we interpret LC_MESSAGES in our own code.
As of today, the PCRE2 library does not seem to use LC_MESSAGES
(their error messages are English-only); and I don't think it uses
LC_CTYPE unless we use "pcre2_maketables()".
Let's make it more obvious which locale categories are actually used
by setting those instead of LC_ALL.
This means that instead of logging the return value of «
setlocale(LC_ALL, "") » (which is an opaque binary string on Linux),
we can log the actual per-category locales that are in effect.
This means that there's no longer really a reason to hardcode a log
for the LC_MESSAGES locale. We're not logging at early startup but
we will log if any locale var had been set at startup.
Commit 046db09f90 (Try to set LC_CTYPE to something UTF-8 capable
(#8031), 2021-06-06) forced UTF-8 encoding if available.
Since c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18) we no
longer override LC_CTYPE, since we no longer use it for anything like
mbrtowc or iswalpha.
However there is one remaining use: our fallbacks to system wcwidth().
If we are started as
LC_ALL=C fish
then wcwidth('😃') will fail to return the correct value, even if
the UTF-8 locale would have been available.
Restore the previous behavior, so locale variables don't affect
emoji width.
This is consistent with the direction in c8001b5023 which stops us
from falling back to ASCII characters if our desired multibyte locale
is missing (that was not the ideal criteria).
In future we should maybe stop using wcwidth().
Commit 8dc3982408 (Always use LC_NUMERIC=C internally (#8204),
2021-10-13) made us use LC_NUMERIC=C internally, to make C library
functions behave in a predictable way.
We no longer use library functions affected by LC_NUMERIC[*]..
Since the effective value of LC_NUMERIC no longer matters, let's
simplify things by using the user locale again, like we do for the
other locale variables.
The printf crate still uses libc::snprintf() which respects LC_NUMERIC,
but it looks like "cargo test" creates a separate process per crate,
and the printf crate does not call setlocale().
* since c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18)
we always use UTF-8, which simplifies docs.
* emphasize that we (as of an earlier commit) document only the locale
variables actually used by fish. We could change this in future,
as long as the docs make it obvious whether it's about fish or
external programs.
* make things a bit more concise
* delete a stale comment - missing encoding support is no longer a problem
We may have used LC_COLLATE in the past via libc functions but I
don't think we do today. In future, we could document the variables
not used by fish, but we should make it obvious what we're documenting.
These include variables that are ignored by fish, which seems
questionable for __fish_set_locale? The the effect seems benign
because __fish_set_locale will do nothing if any of those variables
is defined. Maybe we can get rid of this function eventually.
Link to history, printf and "builtin _" which are the only(?) users
of LC_TIME, LC_NUMERIC and LC_MESSAGES respectively (besides the core
equivalent of "builtin _").
Our test driver unsets all LC_* variables as well as LANGUAGE, and
it sets LANG=C, to make errors show up as
set_color: Unknown color 'reset'
rather than
set_color: Unknown color “reset”
It also sets LC_CTYPE which is hardly necessary since c8001b5023
(encoding: use UTF-8 everywhere, 2025-10-18).
The only place where we need it seems to be the use of GNU sed as
called in tests/checks/sphinx-markdown-changelog.fish.
In future we might want to avoid these issues by setting LANG=C.UTF-8
in the test_driver again.
These are obsolete as of c8001b5023 (encoding: use UTF-8 everywhere,
2025-10-18). The only place where we still read the user's LC_CTYPE
is in libc::wcwidth(), but that's kind of a regression -- we should
always be using a UTF-8 LC_CTYPE if possible -- which will be fixed
by a following commit.
Commit c8001b5023 (encoding: use UTF-8 everywhere, 2025-10-18)
removed some places where we fallback to ASCII if no UTF-8 encoding
is available. That fallback may have been a reasonable approximator
for glyph availability in some cases but it's probably also wrong in
many cases (SSH, containers..).
There are some cases where we still have this sort of fallback.
Remove them for consistency.
Also merge them into one warning because some of these lines wrap
on a small (80 column) terminal, which looks weird. The reason they
wrap might be the long prefix ("warning: fish-build-man-pages@0.0.0:").
This fails:
tests/test_driver.py ~/.local/opt/fish/bin tests/checks/config-paths-standalone.fish
because we don't expect to run from a relocatable tree.
Their msgfmt doesn't support --output-file=- yet, so use a temporary
file if "msgfmt" doesn't support "--check-format". Else we should
have GNU gettext which supports both.
See #11982
There is no need to allocate a new box in these cases.
Flagged by nightly clippy.
There's also no need for the box at all; the comment is no longer
valid, especially because Rust const-propagates by default.
Closes#12014
Using gettext by calling it once on initialization and then reusing the
result prevents changes to the messages as when locale variables change.
A call to the gettext implementation should be made every time the
message is used to handle language changes.
Closes#12012
For historical reasons[^1], most of our Rust tests are in src/tests,
which
1. is unconventional (Rust unit tests are supposed to be either in the
same module as the implementation, or in a child module).
This makes them slightly harder to discover, navigate etc.
2. can't test private APIs (motivating some of the "exposed for
testing" comments).
Fix this by moving tests to the corresponding implementation file.
Reviewed with
git show $commit \
--color-moved=dimmed-zebra \
--color-moved-ws=allow-indentation-change
- Shared test-only code lives in
src/tests/prelude.rs,
src/builtins/string/test_helpers.rs
src/universal_notifier/test_helpers.rs
We might want to slim down the prelude in future.
- I put our two benchmarks below tests ("mod tests" followed by "mod bench").
Verified that "cargo +nightly bench --features=benchmark" still
compiles and runs.
[^1]: Separate files are idiomatic in most other languages; also
separate files makes it easy to ignore when navigating the call graph.
("rg --vimgrep | rg -v tests/"). Fortunately, rust-analyzer provides
a setting called references.excludeTests for textDocument/references,
the textDocument/prepareCallHierarchy family, and potentially
textDocument/documentHighlight (which can be used to find all
references in the current file).
Closes#11992
Commit 1fe6b28877 (rustfmt.toml: specify edition to allow 2024 syntax,
2025-10-19) mentions that "cargo fmt" has different behavior than
"rustfmt" before that commit. Probably because when .rustfmt.toml
exists, rustfmt implicitly uses a different edition (2018?) that
doesn't support c"" yet. That commit bumped the edition to 2024,
which caused yet another deviation from "cargo fmt":
Error writing files: failed to resolve mod `tests`: cannot parse /home/johannes/git/fish-shell/src/wutil/tests.rs
error: expected identifier, found reserved keyword `gen`
--> /home/johannes/git/fish-shell/src/tests/topic_monitor.rs:48:9
|
48 | for gen in &mut gens_list {
| ^^^ expected identifier, found reserved keyword
This has since been fixed by
00784248db (Update to rust 2024 edition, 2025-10-22).
Let's add a test so that such changes won't randomly break "rustfmt"
again.
Fix that by using 2021 edition, like we do in Cargo.toml.
In future, rustfmt should probably default to a current edition (or
maybe read the edition from Cargo.toml?) Not yet sure which one is the
upstream issue, maybe https://github.com/rust-lang/rustfmt/issues/5650