T05: fish search path, PowerShell policy and load failure, encoded OSC 7 paths, kept exit status, install read errors
This commit is contained in:
6 files changed
+210
-90
No files matched your search
@@ -13,6 +13,16 @@ else
|
|||||||
[ -r ~/.bashrc ] && builtin source ~/.bashrc
|
[ -r ~/.bashrc ] && builtin source ~/.bashrc
|
||||||
fi
|
fi
|
||||||
_ely_prompt() {
|
_ely_prompt() {
|
||||||
builtin printf '\e]7;file://%s\a\e]2;%s\a' "${PWD//\%/%25}" "${PWD##*/}"
|
builtin local ret=$? LC_ALL=C char url= i title
|
||||||
|
for (( i = 0; i < ${#PWD}; i++ )); do
|
||||||
|
char=${PWD:i:1}
|
||||||
|
case $char in
|
||||||
|
[[:cntrl:]]|%) builtin printf -v char '%%%02X' "'$char" ;;
|
||||||
|
esac
|
||||||
|
url+=$char
|
||||||
|
done
|
||||||
|
title=${PWD##*/}
|
||||||
|
builtin printf '\e]7;file://%s\a\e]2;%s\a' "$url" "${title//[[:cntrl:]]/}"
|
||||||
|
builtin return $ret
|
||||||
}
|
}
|
||||||
PROMPT_COMMAND="_ely_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
|
PROMPT_COMMAND="_ely_prompt${PROMPT_COMMAND:+;$PROMPT_COMMAND}"
|
||||||
@@ -7,6 +7,6 @@ end
|
|||||||
set -e ELY_FISH_XDG_DATA_DIRS
|
set -e ELY_FISH_XDG_DATA_DIRS
|
||||||
if status is-interactive
|
if status is-interactive
|
||||||
function _ely_prompt --on-event fish_prompt
|
function _ely_prompt --on-event fish_prompt
|
||||||
printf '\e]7;file://%s\a' (string replace -a % %25 -- $PWD)
|
printf '\e]7;file://%s\a' (string escape --style=url -- $PWD)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -1,13 +1,15 @@
|
|||||||
# ELYTerminal: wrap the prompt to report folder and title.
|
# ELYTerminal: wrap the prompt to report folder and title.
|
||||||
$global:ElyPrompt = $function:prompt
|
$global:ElyPrompt = $function:prompt
|
||||||
function global:prompt {
|
function global:prompt {
|
||||||
|
$text = & $global:ElyPrompt
|
||||||
$folder = $executionContext.SessionState.Path.CurrentLocation
|
$folder = $executionContext.SessionState.Path.CurrentLocation
|
||||||
if ($folder.Provider.Name -eq 'FileSystem') {
|
if ($folder.Provider.Name -eq 'FileSystem') {
|
||||||
$path = $folder.ProviderPath.Replace('\', '/').Replace('%', '%25')
|
$parts = $folder.ProviderPath.Replace('\', '/') -split '/'
|
||||||
|
$path = ($parts | ForEach-Object { [Uri]::EscapeDataString($_) }) -join '/'
|
||||||
if (-not $path.StartsWith('/')) { $path = "/$path" }
|
if (-not $path.StartsWith('/')) { $path = "/$path" }
|
||||||
$leaf = Split-Path -Leaf $folder.ProviderPath
|
$leaf = (Split-Path -Leaf $folder.ProviderPath) -replace '\p{Cc}', ''
|
||||||
$esc, $bel = [char]27, [char]7
|
$esc, $bel = [char]27, [char]7
|
||||||
[Console]::Write("$esc]7;file://$path$bel$esc]2;$leaf$bel")
|
[Console]::Write("$esc]7;file://$path$bel$esc]2;$leaf$bel")
|
||||||
}
|
}
|
||||||
& $global:ElyPrompt
|
$text
|
||||||
}
|
}
|
||||||
@@ -8,7 +8,15 @@ fi
|
|||||||
[[ -r "${ZDOTDIR:-$HOME}/.zshenv" ]] && builtin source "${ZDOTDIR:-$HOME}/.zshenv"
|
[[ -r "${ZDOTDIR:-$HOME}/.zshenv" ]] && builtin source "${ZDOTDIR:-$HOME}/.zshenv"
|
||||||
if [[ -o interactive ]]; then
|
if [[ -o interactive ]]; then
|
||||||
_ely_precmd() {
|
_ely_precmd() {
|
||||||
builtin printf '\e]7;file://%s\a\e]2;%s\a' "${PWD//\%/%25}" "${${PWD:t}:-/}"
|
builtin local ret=$? LC_ALL=C char url=
|
||||||
|
for char in ${(s::)PWD}; do
|
||||||
|
case $char in
|
||||||
|
[[:cntrl:]]|%) builtin printf -v char '%%%02X' "'$char" ;;
|
||||||
|
esac
|
||||||
|
url+=$char
|
||||||
|
done
|
||||||
|
builtin printf '\e]7;file://%s\a\e]2;%s\a' "$url" "${${${PWD:t}:-/}//[[:cntrl:]]/}"
|
||||||
|
builtin return ret
|
||||||
}
|
}
|
||||||
_ely_preexec() {
|
_ely_preexec() {
|
||||||
builtin printf '\e]2;%s\a' "${1//[[:cntrl:]]/}"
|
builtin printf '\e]2;%s\a' "${1//[[:cntrl:]]/}"
|
||||||
|
|||||||
+18
-6
@@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
env, fs,
|
env, fs,
|
||||||
|
io::ErrorKind,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -50,8 +51,14 @@ impl ShellIntegration {
|
|||||||
pub fn install(dir: &Path) -> anyhow::Result<()> {
|
pub fn install(dir: &Path) -> anyhow::Result<()> {
|
||||||
for (name, text) in SCRIPTS {
|
for (name, text) in SCRIPTS {
|
||||||
let path = dir.join(name);
|
let path = dir.join(name);
|
||||||
if fs::read_to_string(&path).is_ok_and(|kept| kept == text) {
|
match fs::read_to_string(&path) {
|
||||||
continue;
|
Ok(kept) if kept == text => continue,
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(error) if matches!(error.kind(), ErrorKind::NotFound | ErrorKind::InvalidData) => {}
|
||||||
|
Err(error) => {
|
||||||
|
return Err(error)
|
||||||
|
.with_context(|| format!("shell integration: cannot read {}", path.display()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
let folder = path
|
let folder = path
|
||||||
.parent()
|
.parent()
|
||||||
@@ -98,11 +105,12 @@ pub fn launch(shell: &str, cwd: PathBuf, dir: &Path) -> anyhow::Result<Launch> {
|
|||||||
}
|
}
|
||||||
"fish" => {
|
"fish" => {
|
||||||
let previous = env::var("XDG_DATA_DIRS").unwrap_or_default();
|
let previous = env::var("XDG_DATA_DIRS").unwrap_or_default();
|
||||||
let data = if previous.is_empty() {
|
let rest = if previous.is_empty() {
|
||||||
format!("{}:/usr/local/share:/usr/share", script("fish"))
|
"/usr/local/share:/usr/share"
|
||||||
} else {
|
} else {
|
||||||
format!("{}:{previous}", script("fish"))
|
&previous
|
||||||
};
|
};
|
||||||
|
let data = format!("{}:{rest}", dir.display());
|
||||||
launch.env.push(("XDG_DATA_DIRS".into(), data));
|
launch.env.push(("XDG_DATA_DIRS".into(), data));
|
||||||
launch.env.push(("ELY_FISH_XDG_DATA_DIRS".into(), previous));
|
launch.env.push(("ELY_FISH_XDG_DATA_DIRS".into(), previous));
|
||||||
}
|
}
|
||||||
@@ -113,8 +121,12 @@ pub fn launch(shell: &str, cwd: PathBuf, dir: &Path) -> anyhow::Result<Launch> {
|
|||||||
vec![
|
vec![
|
||||||
"-NoLogo".into(),
|
"-NoLogo".into(),
|
||||||
"-NoExit".into(),
|
"-NoExit".into(),
|
||||||
|
"-ExecutionPolicy".into(),
|
||||||
|
"Bypass".into(),
|
||||||
"-Command".into(),
|
"-Command".into(),
|
||||||
format!(". '{file}'"),
|
format!(
|
||||||
|
"try {{ . '{file}' }} catch {{ [Console]::Error.WriteLine(\"ELYTerminal: shell integration failed: $_\"); exit 1 }}"
|
||||||
|
),
|
||||||
],
|
],
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|||||||
+166
-78
@@ -1,10 +1,13 @@
|
|||||||
use std::time::{Duration, Instant};
|
use std::time::{Duration, Instant};
|
||||||
|
|
||||||
use alacritty_terminal::event::Event;
|
use alacritty_terminal::{event::Event, grid::Dimensions, index::Line};
|
||||||
use futures::channel::mpsc::TryRecvError;
|
use futures::channel::mpsc::TryRecvError;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::term::{PtyEvent, spawn};
|
use crate::term::{Pty, PtyEvent, spawn};
|
||||||
|
|
||||||
|
/// A folder name with a space, a percent, a bell and a newline.
|
||||||
|
const INNER: &str = "in ner%\u{7}x\ny";
|
||||||
|
|
||||||
/// A fresh folder under the temp dir, by its real path.
|
/// A fresh folder under the temp dir, by its real path.
|
||||||
fn folder(name: &str) -> PathBuf {
|
fn folder(name: &str) -> PathBuf {
|
||||||
@@ -21,13 +24,27 @@ fn installed(shell: &str) -> String {
|
|||||||
.args(["-c", &format!("command -v {shell}")])
|
.args(["-c", &format!("command -v {shell}")])
|
||||||
.output()
|
.output()
|
||||||
.expect("sh runs");
|
.expect("sh runs");
|
||||||
let path = String::from_utf8(found.stdout).expect("utf-8");
|
|
||||||
assert!(found.status.success(), "{shell} is not installed");
|
assert!(found.status.success(), "{shell} is not installed");
|
||||||
path.trim().to_string()
|
String::from_utf8(found.stdout)
|
||||||
|
.expect("utf-8")
|
||||||
|
.trim()
|
||||||
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Starts `shell` with integration in a fresh folder, cds once, and returns folders and titles it reported.
|
/// A shell started with integration under a fake HOME, and what it reported.
|
||||||
fn reports(shell: &str) -> (PathBuf, Vec<PathBuf>, Vec<String>) {
|
struct Run {
|
||||||
|
shell: String,
|
||||||
|
pty: Pty,
|
||||||
|
root: PathBuf,
|
||||||
|
start: PathBuf,
|
||||||
|
folders: Vec<PathBuf>,
|
||||||
|
titles: Vec<String>,
|
||||||
|
deadline: Instant,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Run {
|
||||||
|
/// Starts `shell` in a fresh folder; `files` go under its HOME first.
|
||||||
|
fn start(shell: &str, files: &[(&str, &str)]) -> Self {
|
||||||
crate::testlog::init();
|
crate::testlog::init();
|
||||||
let root = folder(&format!(
|
let root = folder(&format!(
|
||||||
"shell-{}",
|
"shell-{}",
|
||||||
@@ -38,63 +55,63 @@ fn reports(shell: &str) -> (PathBuf, Vec<PathBuf>, Vec<String>) {
|
|||||||
root.join("home"),
|
root.join("home"),
|
||||||
root.join("start"),
|
root.join("start"),
|
||||||
);
|
);
|
||||||
let inner = start.join("in ner%");
|
fs::create_dir_all(start.join(INNER)).expect("the inner folder");
|
||||||
for made in [&home, &inner] {
|
fs::create_dir_all(&home).expect("home");
|
||||||
fs::create_dir_all(made).expect("a folder");
|
for (name, text) in files {
|
||||||
|
let path = home.join(name);
|
||||||
|
fs::create_dir_all(path.parent().expect("a folder")).expect("a home folder");
|
||||||
|
fs::write(path, text).expect("a home file");
|
||||||
}
|
}
|
||||||
install(&dir).expect("scripts install");
|
install(&dir).expect("scripts install");
|
||||||
let mut launch = launch(shell, start.clone(), &dir).expect("a launch");
|
let mut launch = launch(shell, start.clone(), &dir).expect("a launch");
|
||||||
launch.env.push(("HOME".into(), home.display().to_string()));
|
launch.env.push(("HOME".into(), home.display().to_string()));
|
||||||
let mut pty = spawn(&launch, (80, 24), (8, 16), 100).expect("the shell starts");
|
let pty = spawn(&launch, (100, 30), (8, 16), 100).expect("the shell starts");
|
||||||
let (mut folders, mut titles) = (Vec::new(), Vec::new());
|
Self {
|
||||||
let deadline = Instant::now() + Duration::from_secs(20);
|
shell: shell.into(),
|
||||||
let mut asked = false;
|
pty,
|
||||||
while !folders.contains(&inner) {
|
root,
|
||||||
assert!(
|
start,
|
||||||
Instant::now() < deadline,
|
folders: Vec::new(),
|
||||||
"{shell} reported {folders:?} and {titles:?} on\n{}",
|
titles: Vec::new(),
|
||||||
screen(&pty)
|
deadline: Instant::now() + Duration::from_secs(30),
|
||||||
);
|
|
||||||
match pty.events.try_recv() {
|
|
||||||
Ok(PtyEvent::Folder(folder)) => folders.push(folder),
|
|
||||||
Ok(PtyEvent::Term(Event::Title(title))) => titles.push(title),
|
|
||||||
Ok(PtyEvent::Term(Event::PtyWrite(reply))) => pty.write(reply.into_bytes()),
|
|
||||||
Ok(PtyEvent::Term(Event::ChildExit(status))) => {
|
|
||||||
panic!("{shell} ended, {status}, after {folders:?}")
|
|
||||||
}
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(TryRecvError::Closed) => panic!("{shell}'s events ended"),
|
|
||||||
Err(TryRecvError::Empty) => std::thread::sleep(Duration::from_millis(10)),
|
|
||||||
}
|
|
||||||
if !asked && folders.contains(&start) {
|
|
||||||
pty.write(format!("cd '{}'\r", inner.display()).into_bytes());
|
|
||||||
asked = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pty.write(&b"exit\r"[..]);
|
|
||||||
loop {
|
|
||||||
assert!(
|
|
||||||
Instant::now() < deadline,
|
|
||||||
"{shell} did not exit:\n{}",
|
|
||||||
screen(&pty)
|
|
||||||
);
|
|
||||||
match pty.events.try_recv() {
|
|
||||||
Ok(PtyEvent::Term(Event::ChildExit(_))) | Err(TryRecvError::Closed) => break,
|
|
||||||
Ok(PtyEvent::Term(Event::Title(title))) => titles.push(title),
|
|
||||||
Ok(PtyEvent::Term(Event::PtyWrite(reply))) => pty.write(reply.into_bytes()),
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(TryRecvError::Empty) => std::thread::sleep(Duration::from_millis(10)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
drop(pty);
|
|
||||||
fs::remove_dir_all(&root).expect("cleanup");
|
|
||||||
(start, folders, titles)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The screen's rows, for a failure's message.
|
/// Reads events, answering the shell's terminal queries, until `done` holds.
|
||||||
fn screen(pty: &crate::term::Pty) -> String {
|
fn until(&mut self, what: &str, done: impl Fn(&Self) -> bool) {
|
||||||
use alacritty_terminal::{grid::Dimensions, index::Line};
|
while !done(self) {
|
||||||
let term = pty.term.lock();
|
assert!(
|
||||||
|
Instant::now() < self.deadline,
|
||||||
|
"{}: no {what}; folders {:?}, titles {:?}, screen\n{}",
|
||||||
|
self.shell,
|
||||||
|
self.folders,
|
||||||
|
self.titles,
|
||||||
|
self.screen()
|
||||||
|
);
|
||||||
|
match self.pty.events.try_recv() {
|
||||||
|
Ok(PtyEvent::Folder(folder)) => self.folders.push(folder),
|
||||||
|
Ok(PtyEvent::Term(Event::Title(title))) => self.titles.push(title),
|
||||||
|
Ok(PtyEvent::Term(Event::PtyWrite(reply))) => self.pty.write(reply.into_bytes()),
|
||||||
|
Ok(PtyEvent::Term(Event::ChildExit(status))) => {
|
||||||
|
panic!("{} ended, {status}:\n{}", self.shell, self.screen())
|
||||||
|
}
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(TryRecvError::Closed) => panic!("{}'s events ended", self.shell),
|
||||||
|
Err(TryRecvError::Empty) => std::thread::sleep(Duration::from_millis(10)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send(&self, line: &str) {
|
||||||
|
self.pty.write(format!("{line}\r").into_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn shows(&self, text: &str) -> bool {
|
||||||
|
self.screen().contains(text)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn screen(&self) -> String {
|
||||||
|
let term = self.pty.term.lock();
|
||||||
(0..term.screen_lines())
|
(0..term.screen_lines())
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
let row = &term.grid()[Line(line as i32)];
|
let row = &term.grid()[Line(line as i32)];
|
||||||
@@ -103,6 +120,39 @@ fn screen(pty: &crate::term::Pty) -> String {
|
|||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The prompt's folder, the script loaded by `probe`, then a cd into the inner folder.
|
||||||
|
fn walk(&mut self, probe: &str) {
|
||||||
|
let start = self.start.clone();
|
||||||
|
self.until("first folder", |run| run.folders.contains(&start));
|
||||||
|
assert_eq!(self.folders[0], start);
|
||||||
|
self.send(probe);
|
||||||
|
self.until("loaded script", |run| run.shows("ELY-LOADED"));
|
||||||
|
self.send("cd in*");
|
||||||
|
let inner = start.join(INNER);
|
||||||
|
self.until("inner folder", |run| run.folders.contains(&inner));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn finish(mut self) {
|
||||||
|
self.send("exit");
|
||||||
|
loop {
|
||||||
|
assert!(
|
||||||
|
Instant::now() < self.deadline,
|
||||||
|
"{} did not exit",
|
||||||
|
self.shell
|
||||||
|
);
|
||||||
|
match self.pty.events.try_recv() {
|
||||||
|
Ok(PtyEvent::Term(Event::ChildExit(_))) | Err(TryRecvError::Closed) => break,
|
||||||
|
Ok(PtyEvent::Term(Event::PtyWrite(reply))) => self.pty.write(reply.into_bytes()),
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(TryRecvError::Empty) => std::thread::sleep(Duration::from_millis(10)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let root = self.root.clone();
|
||||||
|
drop(self);
|
||||||
|
fs::remove_dir_all(&root).expect("cleanup");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@@ -111,46 +161,84 @@ fn the_scripts_install_once_and_mend_a_changed_one() {
|
|||||||
install(&dir).expect("installs");
|
install(&dir).expect("installs");
|
||||||
let zshenv = dir.join("zsh/.zshenv");
|
let zshenv = dir.join("zsh/.zshenv");
|
||||||
assert_eq!(fs::read_to_string(&zshenv).expect("written"), SCRIPTS[0].1);
|
assert_eq!(fs::read_to_string(&zshenv).expect("written"), SCRIPTS[0].1);
|
||||||
fs::write(&zshenv, "changed").expect("write");
|
fs::write(&zshenv, [0xff, 0xfe]).expect("write");
|
||||||
install(&dir).expect("installs again");
|
install(&dir).expect("installs again");
|
||||||
assert_eq!(fs::read_to_string(&zshenv).expect("mended"), SCRIPTS[0].1);
|
assert_eq!(fs::read_to_string(&zshenv).expect("mended"), SCRIPTS[0].1);
|
||||||
fs::remove_dir_all(dir).expect("cleanup");
|
fs::remove_dir_all(dir).expect("cleanup");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn zsh_reports_its_folder_and_title() {
|
fn a_script_it_cannot_read_fails_the_install() {
|
||||||
let (start, folders, titles) = reports(&installed("zsh"));
|
use std::os::unix::fs::PermissionsExt;
|
||||||
assert_eq!(folders[0], start);
|
let dir = folder("unreadable");
|
||||||
assert!(titles.contains(&"start".to_string()), "{titles:?}");
|
install(&dir).expect("installs");
|
||||||
|
let bash = dir.join("bash/elyterminal.bash");
|
||||||
|
fs::set_permissions(&bash, fs::Permissions::from_mode(0o200)).expect("200");
|
||||||
|
let error = install(&dir).expect_err("an unreadable script fails");
|
||||||
|
fs::set_permissions(&bash, fs::Permissions::from_mode(0o600)).expect("600");
|
||||||
assert!(
|
assert!(
|
||||||
titles.iter().any(|title| title.starts_with("cd ")),
|
format!("{error:#}").contains("bash/elyterminal.bash"),
|
||||||
"{titles:?}"
|
"{error:#}"
|
||||||
);
|
);
|
||||||
|
fs::remove_dir_all(dir).expect("cleanup");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn bash_reports_its_folder_and_title() {
|
fn zsh_reports_its_folder_byte_for_byte_and_its_titles() {
|
||||||
let (start, folders, titles) = reports(&installed("bash"));
|
let mut run = Run::start(&installed("zsh"), &[]);
|
||||||
assert_eq!(folders[0], start);
|
run.walk("(( $+functions[_ely_precmd] )) && echo ELY-LOAD''ED");
|
||||||
assert!(titles.contains(&"in ner%".to_string()), "{titles:?}");
|
run.until("title", |run| {
|
||||||
|
run.titles.iter().any(|title| title == "in ner%xy")
|
||||||
|
});
|
||||||
|
assert!(
|
||||||
|
run.titles.contains(&"cd in*".to_string()),
|
||||||
|
"{:?}",
|
||||||
|
run.titles
|
||||||
|
);
|
||||||
|
run.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn fish_reports_its_folder_and_keeps_its_own_title() {
|
fn bash_reports_its_folder_byte_for_byte_and_keeps_the_status() {
|
||||||
let (start, folders, titles) = reports(&installed("fish"));
|
let status = r#"PROMPT_COMMAND='printf "USER_STATUS=%s\n" "$?"'"#;
|
||||||
assert_eq!(folders[0], start);
|
let files = [(".bashrc", status), (".bash_profile", status)];
|
||||||
assert!(
|
let mut run = Run::start(&installed("bash"), &files);
|
||||||
titles.iter().any(|title| title.ends_with("start")),
|
run.walk("declare -F _ely_prompt >/dev/null && echo ELY-LOAD''ED");
|
||||||
"{titles:?}"
|
run.until("title", |run| {
|
||||||
);
|
run.titles.iter().any(|title| title == "in ner%xy")
|
||||||
|
});
|
||||||
|
run.send("false");
|
||||||
|
run.until("kept status", |run| run.shows("USER_STATUS=1"));
|
||||||
|
run.finish();
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn fish_loads_its_script_and_restores_its_data_dirs() {
|
||||||
|
let mut run = Run::start(&installed("fish"), &[]);
|
||||||
|
run.walk("functions -q _ely_prompt; and echo ELY-LOAD''ED");
|
||||||
|
let previous = env::var("XDG_DATA_DIRS").unwrap_or_default();
|
||||||
|
run.send(r#"echo "DIRS[$ELY_FISH_XDG_DATA_DIRS][$XDG_DATA_DIRS]""#);
|
||||||
|
let restored = format!("DIRS[][{previous}]");
|
||||||
|
run.until("restored dirs", |run| run.shows(&restored));
|
||||||
|
run.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
#[test]
|
#[test]
|
||||||
fn powershell_reports_its_folder_and_title() {
|
fn powershell_reports_its_folder_byte_for_byte_and_keeps_the_status() {
|
||||||
let (start, folders, titles) = reports(&installed("pwsh"));
|
let prompt = r#"function global:prompt { "USER_SUCCESS=$? > " }"#;
|
||||||
assert_eq!(folders[0], start);
|
let files = [(
|
||||||
assert!(titles.contains(&"in ner%".to_string()), "{titles:?}");
|
".config/powershell/Microsoft.PowerShell_profile.ps1",
|
||||||
|
prompt,
|
||||||
|
)];
|
||||||
|
let mut run = Run::start(&installed("pwsh"), &files);
|
||||||
|
run.walk("if ($global:ElyPrompt) { 'ELY-LOAD' + 'ED' }");
|
||||||
|
run.until("title", |run| {
|
||||||
|
run.titles.iter().any(|title| title == "in ner%xy")
|
||||||
|
});
|
||||||
|
run.send("/usr/bin/false");
|
||||||
|
run.until("kept status", |run| run.shows("USER_SUCCESS=False"));
|
||||||
|
run.finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
|||||||
Reference in new issue
Block a user