diff --git a/Cargo.lock b/Cargo.lock index b41f7048..18bfd901 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -343,6 +343,12 @@ dependencies = [ "windows-sys 0.59.0", ] +[[package]] +name = "comma" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55b672471b4e9f9e95499ea597ff64941a309b2cdbffcc46f2cc5e2d971fd335" + [[package]] name = "console" version = "0.15.11" @@ -1358,6 +1364,18 @@ dependencies = [ "tokio", ] +[[package]] +name = "nix" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" +dependencies = [ + "bitflags", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nu-ansi-term" version = "0.46.0" @@ -1802,6 +1820,19 @@ dependencies = [ "webpki-roots", ] +[[package]] +name = "rexpect" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c1bcd4ac488e9d2d726d147031cceff5cff6425011ff1914049739770fa4726" +dependencies = [ + "comma", + "nix", + "regex", + "tempfile", + "thiserror", +] + [[package]] name = "ring" version = "0.17.14" @@ -1910,6 +1941,7 @@ dependencies = [ "rayon-tracing", "regex", "reqwest", + "rexpect", "rv-cache", "rv-dirs", "rv-ruby", diff --git a/crates/rv/Cargo.toml b/crates/rv/Cargo.toml index dea1e9db..d56ac257 100644 --- a/crates/rv/Cargo.toml +++ b/crates/rv/Cargo.toml @@ -58,3 +58,4 @@ indoc = { workspace = true } fs-err = { workspace = true } camino-tempfile-ext = { workspace = true } mockito = "1.4.0" +rexpect = "0.6.2" diff --git a/crates/rv/tests/integration_tests/common.rs b/crates/rv/tests/integration_tests/common.rs index 47fc0c3f..c10ec3f6 100644 --- a/crates/rv/tests/integration_tests/common.rs +++ b/crates/rv/tests/integration_tests/common.rs @@ -53,7 +53,11 @@ impl RvTest { } pub fn rv_command(&self) -> Command { - let mut cmd = Command::new(env!("CARGO_BIN_EXE_rv")); + self.command(env!("CARGO_BIN_EXE_rv")) + } + + pub fn command>(&self, program: S) -> Command { + let mut cmd = Command::new(program); cmd.current_dir(&self.cwd); cmd.env_clear().envs(&self.env); cmd diff --git a/crates/rv/tests/integration_tests/shell/mod.rs b/crates/rv/tests/integration_tests/shell/mod.rs index 153d6867..42242271 100644 --- a/crates/rv/tests/integration_tests/shell/mod.rs +++ b/crates/rv/tests/integration_tests/shell/mod.rs @@ -1,2 +1,3 @@ mod env_test; mod init_test; +mod zsh_test; diff --git a/crates/rv/tests/integration_tests/shell/zsh_test.rs b/crates/rv/tests/integration_tests/shell/zsh_test.rs new file mode 100644 index 00000000..db3c51cb --- /dev/null +++ b/crates/rv/tests/integration_tests/shell/zsh_test.rs @@ -0,0 +1,100 @@ +use rexpect::{reader::Options, session::PtyReplSession}; + +use crate::common::RvTest; + +fn make_session(test: &RvTest) -> Result> { + let mut cmd = test.command("zsh"); + cmd.arg("--no-rcs").arg("--login").arg("--interactive"); + cmd.env("TERM", "xterm-256color") + .env("COLUMNS", "1000") + .env_remove("RV_TEST_EXE") + .env("HOST", ">>>") + .env("PATH", "/bin"); + let pty_session = rexpect::spawn_with_options( + cmd, + Options { + timeout_ms: Some(1000), + strip_ansi_escape_codes: true, + }, + )?; + let mut session = PtyReplSession { + prompt: "% \r \r\r[PEXPECT]$ ".to_owned(), + pty_session, + quit_command: Some("builtin exit".to_owned()), + echo_on: true, + }; + + session.send_line("prompt restore")?; + session.send_line(r"PS1='[PEXPECT]%(!.#.$) '")?; + session.send_line(r"unset RPROMPT")?; + session.send_line(r"unset PROMPT_COMMAND")?; + + session.wait_for_prompt()?; + session.send_line(&format!( + "eval \"$({} shell init zsh)\"", + test.rv_command().get_program().display() + ))?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + + Ok(session) +} + +#[test] +fn test_no_rubies() -> Result<(), Box> { + let test = RvTest::new(); + let mut session = make_session(&test)?; + session.send_line("mkdir foobartest")?; + session.wait_for_prompt()?; + session.send_line("cd foobartest")?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + session.send_line("cd ..")?; + session.send_line(r"echo '3.4' > foobartest/.ruby-version")?; + session.send_line("cd foobartest")?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + session.send_line(&format!( + "{} ruby pin", + test.rv_command().get_program().display() + ))?; + session.exp_string("/foobartest is pinned to Ruby 3.4")?; + session.wait_for_prompt()?; + session.send_line("cd ..")?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + + Ok(()) +} + +#[test] +fn test_rubies() -> Result<(), Box> { + let test = RvTest::new(); + test.create_ruby_dir("3.3.4"); + test.create_ruby_dir("3.4.1"); + let mut session = make_session(&test)?; + session.send_line("mkdir foobartest")?; + session.wait_for_prompt()?; + session.send_line("cd foobartest")?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + session.send_line("cd ..")?; + session.send_line(r"echo '3.3' > foobartest/.ruby-version")?; + session.send_line("cd foobartest")?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + session.send_line(&format!( + "{} ruby pin", + test.rv_command().get_program().display() + ))?; + session.exp_string("/foobartest is pinned to Ruby 3.3")?; + session.wait_for_prompt()?; + session.send_line("ruby")?; + assert_eq!( + session.wait_for_prompt()?.trim(), + "ruby\r\n3.3.4\r\naarch64-darwin23\r\naarch64\r\ndarwin23" + ); + session.send_line("cd ..")?; + assert_eq!(session.wait_for_prompt()?.trim(), ""); + session.send_line("ruby")?; + assert_eq!( + session.wait_for_prompt()?.trim(), + "ruby\r\n3.4.1\r\naarch64-darwin23\r\naarch64\r\ndarwin23" + ); + + Ok(()) +}