239 lines
8.3 KiB
Rust
239 lines
8.3 KiB
Rust
use itertools::Itertools as _;
|
|
use std::fs::OpenOptions;
|
|
use std::io::Write;
|
|
use std::path::PathBuf;
|
|
use std::{
|
|
fs::{create_dir_all, write},
|
|
path::Path,
|
|
};
|
|
use strum::IntoEnumIterator;
|
|
use strum_macros::EnumIter;
|
|
|
|
use version_compare::Version;
|
|
|
|
use crate::builder::cargo_target_tmpdir;
|
|
use warp::{
|
|
integration_testing::{
|
|
terminal::util::{
|
|
current_shell_starter_and_version, default_histfile_directory, ExpectedOutput,
|
|
},
|
|
view_getters,
|
|
},
|
|
terminal::shell::ShellType,
|
|
};
|
|
use warpui::{App, WindowId};
|
|
|
|
use warp::terminal::shell;
|
|
|
|
pub fn get_input_buffer(
|
|
app: &App,
|
|
window_id: WindowId,
|
|
tab_index: usize,
|
|
pane_index: usize,
|
|
) -> String {
|
|
view_getters::input_view(app, window_id, tab_index, pane_index)
|
|
.read(app, |input, app| input.buffer_text(app))
|
|
}
|
|
|
|
#[derive(EnumIter)]
|
|
pub enum ShellRcType {
|
|
Bash,
|
|
Zsh,
|
|
Fish,
|
|
PowerShell,
|
|
}
|
|
|
|
impl ShellRcType {
|
|
/// Returns the potential paths to the RC file relative to the `home` directory.
|
|
fn rc_file_paths(&self, home_dir: impl AsRef<Path>) -> Vec<PathBuf> {
|
|
let relative_paths = match self {
|
|
ShellRcType::Bash => vec![Path::new(".bash_profile")],
|
|
ShellRcType::Zsh => vec![Path::new(".zshrc")],
|
|
ShellRcType::Fish => vec![Path::new(".config/fish/config.fish")],
|
|
#[cfg(not(windows))]
|
|
ShellRcType::PowerShell => {
|
|
vec![Path::new(
|
|
".config/powershell/Microsoft.PowerShell_profile.ps1",
|
|
)]
|
|
}
|
|
// We need to make sure this works for either editor of PowerShell (PowerShell Core or
|
|
// Windows PowerShell) so just write the file to both.
|
|
#[cfg(windows)]
|
|
ShellRcType::PowerShell => vec![
|
|
Path::new("Documents/PowerShell/Microsoft.PowerShell_profile.ps1"),
|
|
Path::new("Documents/WindowsPowerShell/Microsoft.PowerShell_profile.ps1"),
|
|
],
|
|
};
|
|
relative_paths
|
|
.iter()
|
|
.map(|relative_path| home_dir.as_ref().join(relative_path))
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
/// Sets the location of the ZSH `HISTFILE` to the home directory.
|
|
/// ZSH does not have a default location for the HISTFILE. However, MacOS has a custom `/etc/zshrc`
|
|
/// file that sets the default location of the `HISTFILE` to be located within the home directory.
|
|
/// To ensure we our tests are consistent across platforms, we set the value of `HISTFILE` to
|
|
/// `HOME` in the same way MacOS does.
|
|
pub fn set_zsh_histfile_location(dir: impl AsRef<Path>) {
|
|
let path = ShellRcType::Zsh
|
|
.rc_file_paths(dir)
|
|
.into_iter()
|
|
.exactly_one()
|
|
.expect("zsh only has one RC file path");
|
|
create_dir_all(path.parent().expect("Parent of RC file should exist"))
|
|
.expect("Should be able to create path to RC file");
|
|
|
|
let mut rc_file = OpenOptions::new()
|
|
.append(true)
|
|
.create(true)
|
|
.open(path)
|
|
.expect("Cannot open zshrc file");
|
|
|
|
rc_file
|
|
.write_all("\nHISTFILE=${ZDOTDIR:-$HOME}/.zsh_history".as_bytes())
|
|
.expect("Failed to write to zshrc file");
|
|
}
|
|
|
|
/// Usually, the title is the pwd where the home dir is shortened as "~".
|
|
/// However, this wasn't the case in Fish prior to version 3.4.0, see:
|
|
/// https://github.com/fish-shell/fish-shell/commit/698b8189356c8224443fdfc4399408f932d53aca
|
|
pub(crate) fn tab_title_in_home_dir(home_suffix: &str) -> String {
|
|
let (shell_starter, shell_version) = current_shell_starter_and_version();
|
|
let shell_version = Version::from(&shell_version).expect("shell version must be valid");
|
|
if shell_starter.shell_type() == ShellType::Fish
|
|
&& shell_version < Version::from("3.4.0").expect("shell version must be valid")
|
|
{
|
|
let home_path = Path::new(&cargo_target_tmpdir::get()).join(home_suffix);
|
|
format!(
|
|
"fish {}",
|
|
home_path.to_str().expect("path must be valid unicode")
|
|
)
|
|
} else {
|
|
String::from("~")
|
|
}
|
|
}
|
|
|
|
/// Writes the `rc_contents` into the corresponding RC files depending on the value of
|
|
/// `ShellRcType`.
|
|
pub fn write_rc_files_for_test<P, C>(
|
|
dir: P,
|
|
rc_contents: C,
|
|
shell_rc_types: impl IntoIterator<Item = ShellRcType>,
|
|
) where
|
|
P: AsRef<Path>,
|
|
C: AsRef<str>,
|
|
{
|
|
for rc_type in shell_rc_types.into_iter() {
|
|
let path_ref = dir.as_ref();
|
|
|
|
let paths = rc_type.rc_file_paths(path_ref);
|
|
for path in paths {
|
|
create_dir_all(path.parent().expect("Parent of RC file path should exist"))
|
|
.expect("Should be able to create path to RC file");
|
|
if let Err(e) = write(&path, rc_contents.as_ref()) {
|
|
panic!("Could not write rc file {:?}: {}", path.to_str(), e);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Writes the same `rc_contents` for all possible shell types supported by Warp.
|
|
pub fn write_all_rc_files_for_test<P, C>(dir: P, rc_contents: C)
|
|
where
|
|
P: AsRef<Path>,
|
|
C: AsRef<str>,
|
|
{
|
|
write_rc_files_for_test(dir, rc_contents, ShellRcType::iter())
|
|
}
|
|
|
|
/// Writes a histfile for `shell_types` to the given `dir`.
|
|
///
|
|
/// `commands` are written in the order that they're specified in the given vector; this means the
|
|
/// commands at the beginning of the vector read as if they were executed before commands at the end
|
|
/// of the vector.
|
|
///
|
|
/// Each histfile is written in the `ShellType`'s expected format.
|
|
pub fn write_histfiles_for_test<P>(
|
|
home_dir: P,
|
|
commands: Vec<&'static str>,
|
|
shell_types: impl IntoIterator<Item = ShellType>,
|
|
) where
|
|
P: AsRef<Path>,
|
|
{
|
|
for shell_type in shell_types.into_iter() {
|
|
let histfile_dir = default_histfile_directory(&shell_type, home_dir.as_ref());
|
|
let path_ref = histfile_dir.as_path();
|
|
create_dir_all(path_ref)
|
|
.expect("Should be able to create {shell_type:?} config directories");
|
|
|
|
let path = match shell_type {
|
|
ShellType::Bash => path_ref.join(".bash_history"),
|
|
ShellType::Zsh => path_ref.join(".zsh_history"),
|
|
ShellType::Fish => path_ref.join("fish_history"),
|
|
ShellType::PowerShell => path_ref.join("ConsoleHost_history.txt"),
|
|
};
|
|
|
|
let histfile_contents = match shell_type {
|
|
ShellType::Bash | ShellType::PowerShell => {
|
|
let mut contents = "".to_owned();
|
|
for command in commands.clone() {
|
|
contents += format!("{command}\n").as_str();
|
|
}
|
|
contents
|
|
}
|
|
ShellType::Fish => {
|
|
let mut contents = "".to_owned();
|
|
for command in commands.clone() {
|
|
println!("COMMAND:{command}");
|
|
contents += format!(
|
|
"- cmd: {}\n when: {}\n",
|
|
command,
|
|
chrono::Local::now().timestamp()
|
|
)
|
|
.as_str();
|
|
}
|
|
contents
|
|
}
|
|
ShellType::Zsh => {
|
|
let mut contents = "".to_owned();
|
|
for command in commands.clone() {
|
|
contents +=
|
|
format!(": {}:0;{}\n", chrono::Local::now().timestamp(), command).as_str();
|
|
}
|
|
contents
|
|
}
|
|
};
|
|
println!("histfile path:{:?}", &path);
|
|
if let Err(e) = write(&path, histfile_contents.as_bytes()) {
|
|
panic!("Could not write histfile {:?}: {}", path.to_str(), e);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Returns the string (ie. expected output etc) for the shell currently used for testing.
|
|
pub fn per_shell_output(
|
|
per_shell_output: Vec<(shell::ShellType, &str)>,
|
|
) -> impl ExpectedOutput + '_ {
|
|
let (starter, _) = current_shell_starter_and_version();
|
|
for (shell_type, output) in per_shell_output {
|
|
if starter.shell_type() == shell_type {
|
|
return Some(output);
|
|
}
|
|
}
|
|
None
|
|
}
|
|
|
|
/// Indicates a test that currently does not work in powershell. As part of CORE-2303, we should
|
|
/// eventually be removing all uses of this function.
|
|
pub fn skip_if_powershell_core_2303() -> bool {
|
|
let (starter, _) = current_shell_starter_and_version();
|
|
!matches!(starter.shell_type(), ShellType::PowerShell)
|
|
}
|
|
|
|
/// Gets the name of the system user for which the test binary is running.
|
|
pub fn get_local_user() -> String {
|
|
whoami::username()
|
|
}
|