This concerns edge cases when executing a function. Historically, when we parse
fish script, we identify early whether or not it's a function; but only record
the function's name and not its properties (i.e. not its source).
This means that the function can change between parsing and execution. Example:
function foo; echo alpha; end
foo (function foo; echo beta; end)
This has historically output "beta" because the function is replaced as part of
its own arguments.
Worse is if the function is deleted:
function foo; echo alpha; end
foo (functions --erase foo)
This outputs an error but in an awkward place; that's OK since it's very rare.
Let's codify this behavior since someone might be depending on it.
Previously, for Processes which were BlockNodes, we stored the node separately
in the Process via an Option; just promote this to a real field of the
ProcessType::BlockNode.
No user visible changes expected.
Our docker tests are currently broken since we no longer seem to install
"build_root". When I sidestep this issue for now and run
sudo docker/docker_run_tests.sh --shell-before docker/focal.Dockerfile
cd /fish-source
CARGO_TARGET_DIR=$HOME/out
tests/test_driver.py ~/out/debug
the test driver fails in various ways.
This is because Ubuntu Focal provides Python 3.8.
Fix some of the typing incompatibilities. Fix a hang in tests like
"tests/checks/init-command.fish"; apparently fish believes it's interactive
because Python 3.8's create_subprocess_shell() makes the fish child believe
that stdin is a TTY, so it's implicitly interactive.
Our docker/docker_run_tests.sh script runs tests in a container with the fish
source tree mounted as read-only. We have a hack to speed up repeated runs
of the check-all-fish-files test that assumes the source tree is writable.
Paper over this by silencing the error for now.
There were no remaining checks left to match stderr:1:
touch: cannot touch '/fish-source/tests/.last-check-all-files': Read-only file system
Commit bf65b9e3a7 (Change `gettext` paths to be relocatable (#11195),
2025-03-30) is difficult to follow because it combines code movement with
two behavior changes. Our parent commit fixed the first behavior change.
The second behavior change made us tolerate trailing NUL bytes in LOCALEDIR.
This was motivated because conda wants to use binary patching to change
the effective prefix at runtime, presumably so the user can move around the
"fish" executable program arbitraily.
This turned out to be unnecessary because fish is already "relocatable"
(see our parent commit).
Let's remove the special case for LOCALEDIR. Treat it like other paths.
Closes#11574Closes#11474
Fish uses this logic to find paths to functions etc.:
1. if $(status fish-path) starts with $CARGO_MANIFEST_DIR,
we use $CARGO_MANIFEST_DIR/share etc.
Aside: this also has the unintended effect that "cmake
-DCMAKE_INSTALL_PREFIX=$PWD/prefix" will not use "$PWD/prefix/share" but
"$PWD/share", at least since eacbd6156d (Port and adopt main written in
Rust, 2023-08-18).
2. Else if $(status fish-path) ends with "bin/fish",
and $(status fish-path)/../share/fish exists, we use that, since 4912967eab
(Large set of changes related to making fish relocatable, and improving
the build and install story, 2012-07-08)
3. Else if $(status fish-path) ends in "fish" (which is very likely),
and $(status fish-path)/share exists, we use that, since
c2a8de4873 (Make fish find config directories in source tree, 2016-09-23).
I think this is for running (without installing) in-tree builds ("cmake .");
this is not recommended but it is used, see
https://github.com/fish-shell/fish-shell/pull/10330
4. If none of the above worked, either because the fish binary has been
moved into a weird directory, or if we fail to get $(status fish-path)
(e.g. on OpenBSD, where "argv[0]" is not available),
then we fall back to reasonable default paths determined at compiled
time.
These paths include data_dir=$PREFIX/share.
We recently added locale_dir too in bf65b9e3a7 (Change `gettext` paths
to be relocatable (#11195), 2025-03-30).
In case 1, we use locale: manifest_dir.join("share/locale"),
In case 2 and 3, we use locale: data_dir.join("locale"),
In case 4, we use locale: data_dir.join("share"),
The last one seems wrong (there is not "/usr/share/share"). Fix that.
Alternatively, we could revert bf65b9e3a7 (and redo the parts we want to keep).
This uses Python's `asyncio` to run tests in parallel, which speeds up test
execution significantly.
The timeout is removed. It would be possible to add a timeout to
`asyncio.as_completed()` if we want that.
This was only used to cache `fish_test_driver`, which can be built in a few tens
of milliseconds on most systems.
This avoids using outdated binaries for testing and simplifies the code and its
usage.
If no `--cachedir` is specifed for `test_driver.py`, it would build
`fish_test_helper` once per test it runs. This is unnecessary. Instead, build it
once in the beginning before running any tests and then use the binary in all
tests.
When the informative status is disabled, the stashstate variable was
set to 1 if the $git_dir/logs/refs/stash file existed and was readable,
even if the file itself was empty (i.e., no stashes). Now the stashstate
variable is set only if the file is NOT empty.
Commit 22c0054c1e (Add check to test all fish files with -n, 2020-02-17)
added a test that runs "fish --no-execute" on all fish files in share/**.fish
Commit 329cd7d429 (Make functions, completions and tests resilient to
running on an embed-data fish, 2025-03-25) change this to run it on **.fish.
Evidently "the tests are exempt because they contain syntax errors" is no
longer true - this is because we have since changed those files to recursively
run 'fish -c "syntax-error"', which makes it easier to test for multiple
syntax-errors in a single test file. Remove the comment.
Globbing everything seems a bit crass, and there's no explicit
motivation.
$ time find -name '*.fish' >/dev/null
Executed in 431.93 millis
$ time find * -name '*.fish' >/dev/null
Executed in 39.98 millis
Let's go back to testing only directories where we currently have Git-tracked
fish files. This makes uncached "check-all-fish-files.fish" go from 26
seconds to 5 seconds.
According to commit 8a07db8e8f (Revert "Revert "Speed up check-all-fish-files
when executed locally"", 2021-03-06), we can assume "find -newer" is supported.
check-all-fish-files takes a long time (>20 seconds here); which is why we
have a (hacky) optimization to avoid checking files we already checked.
This optimization hasn't worked since commit e96b6e157c (Remove TMPDIR
dependency from tests/, 2021-07-30) which started each test invocation in
a private tmpdir. Fix that.
The optimization is useful because this test is, by far, the bottleneck
for parallel test execution (#11561):
$ cargo b && time tests/test_driver.py target/debug
...
checks/tmux-complete.fish PASSED 8465 ms
checks/check-completions.fish PASSED 10948 ms
checks/check-sphinx.fish PASSED 12949 ms
checks/check-all-fish-files.fish PASSED 29828 ms
200 / 200 passed (0 skipped)
________________________________________________________
Executed in 31.00 secs fish external
usr time 81.02 secs 462.00 micros 81.02 secs
sys time 26.41 secs 272.00 micros 26.41 secs
A cache miss for check-all-fish-files.fish takes 24 seconds (though the
grandchild commit will speed this up), a cache hit 0.5 seconds.
Most Git commands take arbitrary revisions. AFAICT, we usually want the same
order, e.g. list local branches before remote branches before commit IDs etc.
I think there is no particular reason why this order is inconsistent between
various subcommands.
Let's extract a function. This standardizes the order and adds various
revision-types that were missing for some subcommands.
This variable is never defined. It was copied from Git's
contrib/completion/git-completion.bash where $match is probably equivalent
to $(commandline -t). I could not measure a significant speedup from passing
this filter to "git for-each-ref", so let's remove it for now.