diff --git a/Cargo.lock b/Cargo.lock index 4ae4ad04..5932578a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -14,15 +14,6 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "aho-corasick" -version = "1.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" -dependencies = [ - "memchr", -] - [[package]] name = "allocator-api2" version = "0.2.18" @@ -84,21 +75,6 @@ version = "1.0.86" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b3d1d046238990b9cf5bcde22a3fb3584ee5cf65fb2765f454ed428c7a0063da" -[[package]] -name = "assert_cmd" -version = "2.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed72493ac66d5804837f480ab3766c72bdfab91a65e565fc54fa9e42db0073a8" -dependencies = [ - "anstyle", - "bstr", - "doc-comment", - "predicates", - "predicates-core", - "predicates-tree", - "wait-timeout", -] - [[package]] name = "autocfg" version = "1.3.0" @@ -117,17 +93,6 @@ version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" -[[package]] -name = "bstr" -version = "1.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05efc5cfd9110c8416e471df0e96702d58690178e206e61b7173706673c93706" -dependencies = [ - "memchr", - "regex-automata", - "serde", -] - [[package]] name = "cassowary" version = "0.3.0" @@ -248,18 +213,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "difflib" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6184e33543162437515c2e2b48714794e37845ec9851711914eec9d308f6ebe8" - -[[package]] -name = "doc-comment" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea41bba32d969b513997752735605054bc0dfa92b4c56bf1189f2e174be7a10" - [[package]] name = "either" version = "1.13.0" @@ -284,15 +237,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "float-cmp" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4" -dependencies = [ - "num-traits", -] - [[package]] name = "fsevent-sys" version = "4.1.0" @@ -438,12 +382,6 @@ dependencies = [ "windows-sys 0.48.0", ] -[[package]] -name = "normalize-line-endings" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" - [[package]] name = "notify" version = "6.1.1" @@ -473,15 +411,6 @@ dependencies = [ "notify", ] -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -527,36 +456,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "predicates" -version = "3.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68b87bfd4605926cdfefc1c3b5f8fe560e3feca9d5552cf68c466d3d8236c7e8" -dependencies = [ - "anstyle", - "difflib", - "float-cmp", - "normalize-line-endings", - "predicates-core", - "regex", -] - -[[package]] -name = "predicates-core" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b794032607612e7abeb4db69adb4e33590fa6cf1149e95fd7cb00e634b92f174" - -[[package]] -name = "predicates-tree" -version = "1.0.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368ba315fb8c5052ab692e68a0eefec6ec57b23a36959c14496f0b0df2c0cecf" -dependencies = [ - "predicates-core", - "termtree", -] - [[package]] name = "proc-macro2" version = "1.0.86" @@ -614,47 +513,16 @@ dependencies = [ "bitflags 2.6.0", ] -[[package]] -name = "regex" -version = "1.10.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" - [[package]] name = "rustlings" version = "6.1.0" dependencies = [ "anyhow", - "assert_cmd", "clap", "crossterm", "hashbrown", "notify-debouncer-mini", "os_pipe", - "predicates", "ratatui", "rustlings-macros", "serde", @@ -829,12 +697,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "termtree" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3369f5ac52d5eb6ab48c6b4ffdc8efbcad6b89c765749064ba298f2c68a16a76" - [[package]] name = "toml_datetime" version = "0.6.6" @@ -898,15 +760,6 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" -[[package]] -name = "wait-timeout" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f200f5b12eb75f8c1ed65abd4b2db8a6e1b138a20de009dacee265a2498f3f6" -dependencies = [ - "libc", -] - [[package]] name = "walkdir" version = "2.5.0" diff --git a/Cargo.toml b/Cargo.toml index 17989238..ddf3c703 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -58,10 +58,6 @@ serde_json = "1.0.120" serde.workspace = true toml_edit.workspace = true -[dev-dependencies] -assert_cmd = "2.0.14" -predicates = "3.1.0" - [profile.release] panic = "abort" diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs index 7d30467b..d5afd2ca 100644 --- a/tests/integration_tests.rs +++ b/tests/integration_tests.rs @@ -1,134 +1,189 @@ -use assert_cmd::prelude::*; -use std::process::Command; +use std::{ + env::{self, consts::EXE_SUFFIX}, + process::{Command, Stdio}, + str::from_utf8, +}; + +#[derive(Default)] +struct Cmd<'a> { + current_dir: Option<&'a str>, + args: &'a [&'a str], + stdout: Option<&'a str>, + full_stdout: bool, +} + +impl<'a> Cmd<'a> { + #[inline] + fn current_dir(&mut self, current_dir: &'a str) -> &mut Self { + self.current_dir = Some(current_dir); + self + } + + #[inline] + fn args(&mut self, args: &'a [&'a str]) -> &mut Self { + self.args = args; + self + } + + #[inline] + fn stdout(&mut self, stdout: &'a str) -> &mut Self { + self.stdout = Some(stdout); + self + } + + #[inline] + fn full_stdout(&mut self) -> &mut Self { + self.full_stdout = true; + self + } + + fn assert(&self, success: bool) { + let rustlings_bin = { + let mut path = env::current_exe().unwrap(); + // Pop test binary name + path.pop(); + // Pop `/deps` + path.pop(); + + path.push("rustlings"); + let mut path = path.into_os_string(); + path.push(EXE_SUFFIX); + path + }; + + let mut cmd = Command::new(rustlings_bin); + + if let Some(current_dir) = self.current_dir { + cmd.current_dir(current_dir); + } + + cmd.args(self.args) + .stdin(Stdio::null()) + .stderr(Stdio::null()); + + let status = if let Some(expected_stdout) = self.stdout { + let output = cmd.output().unwrap(); + let stdout = from_utf8(&output.stdout).unwrap(); + + if self.full_stdout { + assert_eq!(stdout, expected_stdout); + } else { + assert!(stdout.contains(expected_stdout)); + } + + output.status + } else { + cmd.stdout(Stdio::null()).status().unwrap() + }; + + assert_eq!(status.success(), success); + } + + #[inline] + fn success(&self) { + self.assert(true); + } + + #[inline] + fn fail(&self) { + self.assert(false); + } +} #[test] fn fails_when_in_wrong_dir() { - Command::cargo_bin("rustlings") - .unwrap() - .current_dir("tests/") - .assert() - .code(1); + Cmd::default().current_dir("tests").fail(); } #[test] fn run_single_compile_success() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "compSuccess"]) - .current_dir("tests/fixture/success/") - .assert() + Cmd::default() + .current_dir("tests/fixture/success") + .args(&["run", "compSuccess"]) .success(); } #[test] fn run_single_compile_failure() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "compFailure"]) - .current_dir("tests/fixture/failure/") - .assert() - .code(1); + Cmd::default() + .current_dir("tests/fixture/failure") + .args(&["run", "compFailure"]) + .fail(); } #[test] fn run_single_test_success() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testSuccess"]) - .current_dir("tests/fixture/success/") - .assert() + Cmd::default() + .current_dir("tests/fixture/success") + .args(&["run", "testSuccess"]) .success(); } #[test] fn run_single_test_failure() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testFailure"]) - .current_dir("tests/fixture/failure/") - .assert() - .code(1); + Cmd::default() + .current_dir("tests/fixture/failure") + .args(&["run", "testFailure"]) + .fail(); } #[test] fn run_single_test_not_passed() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testNotPassed.rs"]) - .current_dir("tests/fixture/failure/") - .assert() - .code(1); + Cmd::default() + .current_dir("tests/fixture/failure") + .args(&["run", "testNotPassed.rs"]) + .fail(); } #[test] fn run_single_test_no_exercise() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "compNoExercise.rs"]) + Cmd::default() .current_dir("tests/fixture/failure") - .assert() - .code(1); + .args(&["run", "compNoExercise.rs"]) + .fail(); } #[test] fn reset_single_exercise() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["reset", "intro1"]) - .assert() - .code(0); + Cmd::default().args(&["reset", "intro1"]).success(); } #[test] fn reset_no_exercise() { - Command::cargo_bin("rustlings") - .unwrap() - .arg("reset") - .assert() - .code(2) - .stderr(predicates::str::contains( - "required arguments were not provided", - )); + Cmd::default().args(&["reset"]).fail(); } #[test] fn get_hint_for_single_test() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["hint", "testFailure"]) + Cmd::default() .current_dir("tests/fixture/failure") - .assert() - .code(0) - .stdout("Hello!\n"); + .args(&["hint", "testFailure"]) + .stdout("Hello!\n") + .full_stdout() + .success(); } #[test] fn run_compile_exercise_does_not_prompt() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "pending_exercise"]) + Cmd::default() .current_dir("tests/fixture/state") - .assert() - .code(0); + .args(&["run", "pending_exercise"]) + .success(); } #[test] fn run_test_exercise_does_not_prompt() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "pending_test_exercise"]) + Cmd::default() .current_dir("tests/fixture/state") - .assert() - .code(0); + .args(&["run", "pending_test_exercise"]) + .success(); } #[test] fn run_single_test_success_with_output() { - Command::cargo_bin("rustlings") - .unwrap() - .args(["run", "testSuccess"]) - .current_dir("tests/fixture/success/") - .assert() - .code(0) - .stdout(predicates::str::contains("THIS TEST TOO SHALL PASS")); + Cmd::default() + .current_dir("tests/fixture/success") + .args(&["run", "testSuccess"]) + .stdout("\nTHIS TEST TOO SHALL PASS\n") + .success(); }