diff --git a/Cargo.lock b/Cargo.lock index 6b6ef5ee0e..ff23500396 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -246,6 +246,18 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "904dfeac50f3cdaba28fc6f57fdcddb75f49ed61346676a78c4ffe55877802fd" +[[package]] +name = "bigdecimal" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6aaf33151a6429fe9211d1b276eafdf70cdff28b071e76c0b0e1503221ea3744" +dependencies = [ + "num-bigint 0.4.3", + "num-integer", + "num-traits", + "serde", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -908,40 +920,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "engine-q" -version = "0.1.0" -dependencies = [ - "assert_cmd", - "crossterm", - "crossterm_winapi", - "ctrlc", - "log", - "miette", - "nu-ansi-term", - "nu-cli", - "nu-color-config", - "nu-command", - "nu-engine", - "nu-json", - "nu-parser", - "nu-path", - "nu-plugin", - "nu-pretty-hex", - "nu-protocol", - "nu-system", - "nu-table", - "nu-term-grid", - "nu_plugin_example", - "nu_plugin_gstat", - "nu_plugin_inc", - "nu_plugin_query", - "pretty_assertions", - "pretty_env_logger", - "reedline", - "tempfile", -] - [[package]] name = "env_logger" version = "0.7.1" @@ -955,6 +933,16 @@ dependencies = [ "termcolor", ] +[[package]] +name = "env_logger" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "log", + "regex", +] + [[package]] name = "erased-serde" version = "0.3.16" @@ -1212,6 +1200,18 @@ dependencies = [ "wasi 0.10.0+wasi-snapshot-preview1", ] +[[package]] +name = "getset" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e45727250e75cc04ff2846a66397da8ef2b3db8e40e0cef4df67950a07621eb9" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "ghost" version = "0.1.2" @@ -1275,6 +1275,16 @@ dependencies = [ "tracing", ] +[[package]] +name = "hamcrest2" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49f837c62de05dc9cc71ff6486cd85de8856a330395ae338a04bfcefe5e91075" +dependencies = [ + "num 0.2.1", + "regex", +] + [[package]] name = "hash32" version = "0.1.1" @@ -2020,6 +2030,45 @@ dependencies = [ "winapi", ] +[[package]] +name = "nu" +version = "0.1.0" +dependencies = [ + "assert_cmd", + "crossterm", + "crossterm_winapi", + "ctrlc", + "hamcrest2", + "itertools", + "log", + "miette", + "nu-ansi-term", + "nu-cli", + "nu-color-config", + "nu-command", + "nu-engine", + "nu-json", + "nu-parser", + "nu-path", + "nu-plugin", + "nu-pretty-hex", + "nu-protocol", + "nu-system", + "nu-table", + "nu-term-grid", + "nu-test-support", + "nu_plugin_example", + "nu_plugin_gstat", + "nu_plugin_inc", + "nu_plugin_query", + "pretty_assertions", + "pretty_env_logger", + "reedline", + "rstest", + "serial_test", + "tempfile", +] + [[package]] name = "nu-ansi-term" version = "0.42.0" @@ -2073,10 +2122,12 @@ dependencies = [ "csv", "dialoguer", "digest 0.10.0", + "dirs-next", "dtparse", "eml-parser", "encoding_rs", "glob", + "hamcrest2", "htmlescape", "ical", "indexmap", @@ -2098,10 +2149,13 @@ dependencies = [ "nu-system", "nu-table", "nu-term-grid", + "nu-test-support", "num 0.4.0", "pathdiff", "polars", "quick-xml 0.22.0", + "quickcheck", + "quickcheck_macros", "rand 0.8.4", "rayon", "regex", @@ -2251,6 +2305,22 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "nu-test-support" +version = "0.43.0" +dependencies = [ + "bigdecimal", + "chrono", + "getset", + "glob", + "hamcrest2", + "indexmap", + "nu-path", + "nu-protocol", + "num-bigint 0.4.3", + "tempfile", +] + [[package]] name = "nu_plugin_example" version = "0.1.0" @@ -2339,6 +2409,7 @@ dependencies = [ "autocfg", "num-integer", "num-traits", + "serde", ] [[package]] @@ -2875,7 +2946,7 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "926d36b9553851b8b0005f1275891b392ee4d2d833852c417ed025477350fb9d" dependencies = [ - "env_logger", + "env_logger 0.7.1", "log", ] @@ -2893,6 +2964,30 @@ dependencies = [ "unicode-width", ] +[[package]] +name = "proc-macro-error" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" +dependencies = [ + "proc-macro-error-attr", + "proc-macro2", + "quote", + "syn", + "version_check 0.9.3", +] + +[[package]] +name = "proc-macro-error-attr" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +dependencies = [ + "proc-macro2", + "quote", + "version_check 0.9.3", +] + [[package]] name = "proc-macro-hack" version = "0.5.19" @@ -2948,6 +3043,28 @@ dependencies = [ "memchr", ] +[[package]] +name = "quickcheck" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "588f6378e4dd99458b60ec275b4477add41ce4fa9f64dcba6f15adccb19b50d6" +dependencies = [ + "env_logger 0.8.4", + "log", + "rand 0.8.4", +] + +[[package]] +name = "quickcheck_macros" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b22a693222d716a9587786f37ac3f6b4faedb5b80c23914e7303ff5a1d8016e9" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "quote" version = "1.0.10" @@ -3224,6 +3341,19 @@ dependencies = [ "xmlparser", ] +[[package]] +name = "rstest" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d912f35156a3f99a66ee3e11ac2e0b3f34ac85a07e05263d05a7e2c8810d616f" +dependencies = [ + "cfg-if", + "proc-macro2", + "quote", + "rustc_version", + "syn", +] + [[package]] name = "rust-argon2" version = "0.8.3" @@ -3487,6 +3617,28 @@ dependencies = [ "yaml-rust", ] +[[package]] +name = "serial_test" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0bccbcf40c8938196944a3da0e133e031a33f4d6b72db3bda3cc556e361905d" +dependencies = [ + "lazy_static", + "parking_lot", + "serial_test_derive", +] + +[[package]] +name = "serial_test_derive" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2acd6defeddb41eb60bb468f8825d0cfd0c2a76bc03bfd235b6a1dc4f6a1ad5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "servo_arc" version = "0.1.1" diff --git a/Cargo.toml b/Cargo.toml index 1029f669a9..2c907d1e08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,8 +1,8 @@ [package] -name = "engine-q" +name = "nu" version = "0.1.0" edition = "2021" -default-run = "engine-q" +default-run = "nu" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -36,6 +36,7 @@ nu-plugin = { path = "./crates/nu-plugin", optional = true } nu-system = { path = "./crates/nu-system"} nu-table = { path = "./crates/nu-table" } nu-term-grid = { path = "./crates/nu-term-grid" } + nu-ansi-term = "0.42.0" nu-color-config = { path = "./crates/nu-color-config" } miette = "3.0.0" @@ -52,11 +53,14 @@ nu_plugin_gstat = { version = "0.1.0", path = "./crates/nu_plugin_gstat", option nu_plugin_query = { version = "0.1.0", path = "./crates/nu_plugin_query", optional = true } [dev-dependencies] +nu-test-support = { path="./crates/nu-test-support" } tempfile = "3.2.0" assert_cmd = "2.0.2" pretty_assertions = "1.0.0" - -[build-dependencies] +serial_test = "0.5.1" +hamcrest2 = "0.3.0" +rstest = "0.12.0" +itertools = "0.10.3" [features] plugin = ["nu-plugin", "nu-parser/plugin", "nu-command/plugin", "nu-protocol/plugin", "nu-engine/plugin"] @@ -119,5 +123,5 @@ required-features = ["query"] # Main nu binary [[bin]] -name = "engine-q" +name = "nu" path = "src/main.rs" diff --git a/crates/nu-command/Cargo.toml b/crates/nu-command/Cargo.toml index c81b5f9556..3bd5313aab 100644 --- a/crates/nu-command/Cargo.toml +++ b/crates/nu-command/Cargo.toml @@ -15,6 +15,7 @@ nu-pretty-hex = { path = "../nu-pretty-hex" } nu-protocol = { path = "../nu-protocol" } nu-table = { path = "../nu-table" } nu-term-grid = { path = "../nu-term-grid" } +nu-test-support = { path = "../nu-test-support" } nu-parser = { path = "../nu-parser" } nu-system = { path = "../nu-system" } # nu-ansi-term = { path = "../nu-ansi-term" } @@ -95,3 +96,9 @@ dataframe = ["polars", "num"] [build-dependencies] shadow-rs = "0.8.1" + +[dev-dependencies] +hamcrest2 = "0.3.0" +dirs-next = "2.0.0" +quickcheck = "1.0.3" +quickcheck_macros = "1.0.0" \ No newline at end of file diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index 35a912bb0b..d94f1b9ed8 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -1028,13 +1028,25 @@ fn compute(size: i64, unit: Unit, span: Span) -> Value { val: size * 1000 * 1000 * 1000 * 60 * 60, span, }, - Unit::Day => Value::Duration { - val: size * 1000 * 1000 * 1000 * 60 * 60 * 24, - span, + Unit::Day => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24) { + Some(val) => Value::Duration { val, span }, + None => Value::Error { + error: ShellError::SpannedLabeledError( + "duration too large".into(), + "duration too large".into(), + span, + ), + }, }, - Unit::Week => Value::Duration { - val: size * 1000 * 1000 * 1000 * 60 * 60 * 24 * 7, - span, + Unit::Week => match size.checked_mul(1000 * 1000 * 1000 * 60 * 60 * 24 * 7) { + Some(val) => Value::Duration { val, span }, + None => Value::Error { + error: ShellError::SpannedLabeledError( + "duration too large".into(), + "duration too large".into(), + span, + ), + }, }, } } diff --git a/crates/nu-parser/src/type_check.rs b/crates/nu-parser/src/type_check.rs index 2d6bff4bcc..217b2643ec 100644 --- a/crates/nu-parser/src/type_check.rs +++ b/crates/nu-parser/src/type_check.rs @@ -88,7 +88,34 @@ pub fn math_result_type( ) } }, - Operator::Multiply | Operator::Pow => match (&lhs.ty, &rhs.ty) { + Operator::Multiply => match (&lhs.ty, &rhs.ty) { + (Type::Int, Type::Int) => (Type::Int, None), + (Type::Float, Type::Int) => (Type::Float, None), + (Type::Int, Type::Float) => (Type::Float, None), + (Type::Float, Type::Float) => (Type::Float, None), + + (Type::Filesize, Type::Int) => (Type::Filesize, None), + (Type::Int, Type::Filesize) => (Type::Filesize, None), + (Type::Duration, Type::Int) => (Type::Filesize, None), + (Type::Int, Type::Duration) => (Type::Filesize, None), + + (Type::Unknown, _) => (Type::Unknown, None), + (_, Type::Unknown) => (Type::Unknown, None), + _ => { + *op = Expression::garbage(op.span); + ( + Type::Unknown, + Some(ParseError::UnsupportedOperation( + op.span, + lhs.span, + lhs.ty.clone(), + rhs.span, + rhs.ty.clone(), + )), + ) + } + }, + Operator::Pow => match (&lhs.ty, &rhs.ty) { (Type::Int, Type::Int) => (Type::Int, None), (Type::Float, Type::Int) => (Type::Float, None), (Type::Int, Type::Float) => (Type::Float, None), @@ -118,6 +145,9 @@ pub fn math_result_type( (Type::Filesize, Type::Filesize) => (Type::Float, None), (Type::Duration, Type::Duration) => (Type::Float, None), + (Type::Filesize, Type::Int) => (Type::Filesize, None), + (Type::Duration, Type::Int) => (Type::Duration, None), + (Type::Unknown, _) => (Type::Unknown, None), (_, Type::Unknown) => (Type::Unknown, None), _ => { diff --git a/crates/nu-protocol/src/engine/command.rs b/crates/nu-protocol/src/engine/command.rs index 9615e6b5b4..3727723dec 100644 --- a/crates/nu-protocol/src/engine/command.rs +++ b/crates/nu-protocol/src/engine/command.rs @@ -7,9 +7,7 @@ use super::{EngineState, Stack}; pub trait Command: Send + Sync + CommandClone { fn name(&self) -> &str; - fn signature(&self) -> Signature { - Signature::new(self.name()).desc(self.usage()).filter() - } + fn signature(&self) -> Signature; fn usage(&self) -> &str; diff --git a/crates/nu-protocol/src/value/mod.rs b/crates/nu-protocol/src/value/mod.rs index 99d67650a5..29e6a4653e 100644 --- a/crates/nu-protocol/src/value/mod.rs +++ b/crates/nu-protocol/src/value/mod.rs @@ -1122,6 +1122,30 @@ impl Value { val: lhs * rhs, span, }), + (Value::Int { val: lhs, .. }, Value::Filesize { val: rhs, .. }) => { + Ok(Value::Filesize { + val: *lhs * *rhs, + span, + }) + } + (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + Ok(Value::Filesize { + val: *lhs * *rhs, + span, + }) + } + (Value::Int { val: lhs, .. }, Value::Duration { val: rhs, .. }) => { + Ok(Value::Duration { + val: *lhs * *rhs, + span, + }) + } + (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + Ok(Value::Duration { + val: *lhs * *rhs, + span, + }) + } (Value::CustomValue { val: lhs, span }, rhs) => { lhs.operation(*span, Operator::Multiply, op, rhs) } @@ -1220,6 +1244,26 @@ impl Value { Err(ShellError::DivisionByZero(op)) } } + (Value::Filesize { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if *rhs != 0 { + Ok(Value::Filesize { + val: lhs / rhs, + span, + }) + } else { + Err(ShellError::DivisionByZero(op)) + } + } + (Value::Duration { val: lhs, .. }, Value::Int { val: rhs, .. }) => { + if *rhs != 0 { + Ok(Value::Duration { + val: lhs / rhs, + span, + }) + } else { + Err(ShellError::DivisionByZero(op)) + } + } (Value::CustomValue { val: lhs, span }, rhs) => { lhs.operation(*span, Operator::Divide, op, rhs) } diff --git a/crates/nu-test-support/Cargo.toml b/crates/nu-test-support/Cargo.toml new file mode 100644 index 0000000000..8329e24639 --- /dev/null +++ b/crates/nu-test-support/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Nu Project Contributors"] +description = "Support for writing Nushell tests" +edition = "2018" +license = "MIT" +name = "nu-test-support" +version = "0.43.0" + +[lib] +doctest = false + +[dependencies] +nu-path = { path="../nu-path" } +nu-protocol = { path="../nu-protocol" } + +bigdecimal = { package = "bigdecimal", version = "0.3.0", features = ["serde"] } +chrono = "0.4.19" +getset = "0.1.1" +glob = "0.3.0" +indexmap = { version="1.6.1", features=["serde-1"] } +num-bigint = { version="0.4.3", features=["serde"] } +tempfile = "3.2.0" +hamcrest2 = "0.3.0" diff --git a/crates/nu-test-support/src/commands.rs b/crates/nu-test-support/src/commands.rs new file mode 100644 index 0000000000..d59c2f3dad --- /dev/null +++ b/crates/nu-test-support/src/commands.rs @@ -0,0 +1,53 @@ +// use nu_protocol::{ +// ast::{Expr, Expression}, +// Span, Spanned, Type, +// }; + +// pub struct ExternalBuilder { +// name: String, +// args: Vec, +// } + +// impl ExternalBuilder { +// pub fn for_name(name: &str) -> ExternalBuilder { +// ExternalBuilder { +// name: name.to_string(), +// args: vec![], +// } +// } + +// pub fn arg(&mut self, value: &str) -> &mut Self { +// self.args.push(value.to_string()); +// self +// } + +// pub fn build(&mut self) -> ExternalCommand { +// let mut path = crate::fs::binaries(); +// path.push(&self.name); + +// let name = Spanned { +// item: path.to_string_lossy().to_string(), +// span: Span::new(0, 0), +// }; + +// let args = self +// .args +// .iter() +// .map(|arg| Expression { +// expr: Expr::String(arg.to_string()), +// span: Span::new(0, 0), +// ty: Type::Unknown, +// custom_completion: None, +// }) +// .collect::>(); + +// ExternalCommand { +// name: name.to_string(), +// name_tag: Tag::unknown(), +// args: ExternalArgs { +// list: args, +// span: name.span, +// }, +// } +// } +// } diff --git a/crates/nu-test-support/src/fs.rs b/crates/nu-test-support/src/fs.rs new file mode 100644 index 0000000000..bf7d87df77 --- /dev/null +++ b/crates/nu-test-support/src/fs.rs @@ -0,0 +1,277 @@ +use std::io::Read; +use std::ops::Div; +use std::path::{Path, PathBuf}; + +pub struct AbsoluteFile { + inner: PathBuf, +} + +impl AbsoluteFile { + pub fn new(path: impl AsRef) -> AbsoluteFile { + let path = path.as_ref(); + + if !path.is_absolute() { + panic!( + "AbsoluteFile::new must take an absolute path :: {}", + path.display() + ) + } else if path.is_dir() { + // At the moment, this is not an invariant, but rather a way to catch bugs + // in tests. + panic!( + "AbsoluteFile::new must not take a directory :: {}", + path.display() + ) + } else { + AbsoluteFile { + inner: path.to_path_buf(), + } + } + } + + pub fn dir(&self) -> AbsolutePath { + AbsolutePath::new(if let Some(parent) = self.inner.parent() { + parent + } else { + unreachable!("Internal error: could not get parent in dir") + }) + } +} + +impl From for PathBuf { + fn from(file: AbsoluteFile) -> Self { + file.inner + } +} + +pub struct AbsolutePath { + pub inner: PathBuf, +} + +impl AbsolutePath { + pub fn new(path: impl AsRef) -> AbsolutePath { + let path = path.as_ref(); + + if path.is_absolute() { + AbsolutePath { + inner: path.to_path_buf(), + } + } else { + panic!("AbsolutePath::new must take an absolute path") + } + } +} + +impl Div<&str> for &AbsolutePath { + type Output = AbsolutePath; + + fn div(self, rhs: &str) -> Self::Output { + let parts = rhs.split('/'); + let mut result = self.inner.clone(); + + for part in parts { + result = result.join(part); + } + + AbsolutePath::new(result) + } +} + +impl AsRef for AbsolutePath { + fn as_ref(&self) -> &Path { + self.inner.as_path() + } +} + +pub struct RelativePath { + inner: PathBuf, +} + +impl RelativePath { + pub fn new(path: impl Into) -> RelativePath { + let path = path.into(); + + if path.is_relative() { + RelativePath { inner: path } + } else { + panic!("RelativePath::new must take a relative path") + } + } +} + +impl> Div for &RelativePath { + type Output = RelativePath; + + fn div(self, rhs: T) -> Self::Output { + let parts = rhs.as_ref().split('/'); + let mut result = self.inner.clone(); + + for part in parts { + result = result.join(part); + } + + RelativePath::new(result) + } +} +pub trait DisplayPath { + fn display_path(&self) -> String; +} + +impl DisplayPath for AbsolutePath { + fn display_path(&self) -> String { + self.inner.display().to_string() + } +} + +impl DisplayPath for PathBuf { + fn display_path(&self) -> String { + self.display().to_string() + } +} + +impl DisplayPath for str { + fn display_path(&self) -> String { + self.to_string() + } +} + +impl DisplayPath for &str { + fn display_path(&self) -> String { + (*self).to_string() + } +} + +impl DisplayPath for String { + fn display_path(&self) -> String { + self.clone() + } +} + +impl DisplayPath for &String { + fn display_path(&self) -> String { + (*self).to_string() + } +} +pub enum Stub<'a> { + FileWithContent(&'a str, &'a str), + FileWithContentToBeTrimmed(&'a str, &'a str), + EmptyFile(&'a str), +} + +pub fn file_contents(full_path: impl AsRef) -> String { + let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file"); + let mut contents = String::new(); + file.read_to_string(&mut contents) + .expect("can not read file"); + contents +} + +pub fn file_contents_binary(full_path: impl AsRef) -> Vec { + let mut file = std::fs::File::open(full_path.as_ref()).expect("can not open file"); + let mut contents = Vec::new(); + file.read_to_end(&mut contents).expect("can not read file"); + contents +} + +pub fn line_ending() -> String { + #[cfg(windows)] + { + String::from("\r\n") + } + + #[cfg(not(windows))] + { + String::from("\n") + } +} + +pub fn delete_file_at(full_path: impl AsRef) { + let full_path = full_path.as_ref(); + + if full_path.exists() { + std::fs::remove_file(full_path).expect("can not delete file"); + } +} + +pub fn create_file_at(full_path: impl AsRef) -> Result<(), std::io::Error> { + let full_path = full_path.as_ref(); + + if full_path.parent().is_some() { + panic!("path exists"); + } + + std::fs::write(full_path, b"fake data") +} + +pub fn copy_file_to(source: &str, destination: &str) { + std::fs::copy(source, destination).expect("can not copy file"); +} + +pub fn files_exist_at(files: Vec>, path: impl AsRef) -> bool { + files.iter().all(|f| { + let mut loc = PathBuf::from(path.as_ref()); + loc.push(f); + loc.exists() + }) +} + +pub fn delete_directory_at(full_path: &str) { + std::fs::remove_dir_all(PathBuf::from(full_path)).expect("can not remove directory"); +} + +pub fn executable_path() -> PathBuf { + let mut path = binaries(); + path.push("nu"); + path +} + +pub fn root() -> PathBuf { + let manifest_dir = if let Ok(manifest_dir) = std::env::var("CARGO_MANIFEST_DIR") { + PathBuf::from(manifest_dir) + } else { + PathBuf::from(env!("CARGO_MANIFEST_DIR")) + }; + + let test_path = manifest_dir.join("Cargo.lock"); + if test_path.exists() { + manifest_dir + } else { + manifest_dir + .parent() + .expect("Couldn't find the debug binaries directory") + .parent() + .expect("Couldn't find the debug binaries directory") + .to_path_buf() + } +} + +pub fn binaries() -> PathBuf { + let mut build_type = "debug"; + if !cfg!(debug_assertions) { + build_type = "release" + } + + std::env::var("CARGO_TARGET_DIR") + .ok() + .map(|target_dir| PathBuf::from(target_dir).join(&build_type)) + .unwrap_or_else(|| root().join(format!("target/{}", &build_type))) +} + +pub fn fixtures() -> PathBuf { + root().join("tests/fixtures") +} + +pub fn assets() -> PathBuf { + root().join("tests/assets") +} + +pub fn in_directory(str: impl AsRef) -> String { + let path = str.as_ref(); + let path = if path.is_relative() { + root().join(path) + } else { + path.to_path_buf() + }; + + path.display().to_string() +} diff --git a/crates/nu-test-support/src/lib.rs b/crates/nu-test-support/src/lib.rs new file mode 100644 index 0000000000..b0f0685736 --- /dev/null +++ b/crates/nu-test-support/src/lib.rs @@ -0,0 +1,70 @@ +pub mod commands; +pub mod fs; +pub mod macros; +pub mod playground; + +pub struct Outcome { + pub out: String, + pub err: String, +} + +#[cfg(windows)] +pub const NATIVE_PATH_ENV_VAR: &str = "Path"; +#[cfg(not(windows))] +pub const NATIVE_PATH_ENV_VAR: &str = "PATH"; + +#[cfg(windows)] +pub const NATIVE_PATH_ENV_SEPARATOR: char = ';'; +#[cfg(not(windows))] +pub const NATIVE_PATH_ENV_SEPARATOR: char = ':'; + +impl Outcome { + pub fn new(out: String, err: String) -> Outcome { + Outcome { out, err } + } +} + +pub fn pipeline(commands: &str) -> String { + commands + .lines() + .skip(1) + .map(|line| line.trim()) + .collect::>() + .join(" ") + .trim_end() + .to_string() +} + +pub fn shell_os_paths() -> Vec { + let mut original_paths = vec![]; + + if let Some(paths) = std::env::var_os(NATIVE_PATH_ENV_VAR) { + original_paths = std::env::split_paths(&paths).collect::>(); + } + + original_paths +} + +#[cfg(test)] +mod tests { + use super::pipeline; + + #[test] + fn constructs_a_pipeline() { + let actual = pipeline( + r#" + open los_tres_amigos.txt + | from-csv + | get rusty_luck + | str to-int + | math sum + | echo "$it" + "#, + ); + + assert_eq!( + actual, + r#"open los_tres_amigos.txt | from-csv | get rusty_luck | str to-int | math sum | echo "$it""# + ); + } +} diff --git a/crates/nu-test-support/src/macros.rs b/crates/nu-test-support/src/macros.rs new file mode 100644 index 0000000000..f692754411 --- /dev/null +++ b/crates/nu-test-support/src/macros.rs @@ -0,0 +1,170 @@ +#[macro_export] +macro_rules! nu { + (cwd: $cwd:expr, $path:expr, $($part:expr),*) => {{ + use $crate::fs::DisplayPath; + + let path = format!($path, $( + $part.display_path() + ),*); + + nu!($cwd, &path) + }}; + + (cwd: $cwd:expr, $path:expr) => {{ + nu!($cwd, $path) + }}; + + ($cwd:expr, $path:expr) => {{ + pub use itertools::Itertools; + pub use std::error::Error; + pub use std::io::prelude::*; + pub use std::process::{Command, Stdio}; + pub use $crate::NATIVE_PATH_ENV_VAR; + + // let commands = &*format!( + // " + // cd \"{}\" + // {} + // exit", + // $crate::fs::in_directory($cwd), + // $crate::fs::DisplayPath::display_path(&$path) + // ); + + let test_bins = $crate::fs::binaries(); + + let cwd = std::env::current_dir().expect("Could not get current working directory."); + let test_bins = nu_path::canonicalize_with(&test_bins, cwd).unwrap_or_else(|e| { + panic!( + "Couldn't canonicalize dummy binaries path {}: {:?}", + test_bins.display(), + e + ) + }); + + let mut paths = $crate::shell_os_paths(); + paths.insert(0, test_bins); + + let path = $path.lines().collect::>().join("; "); + + let paths_joined = match std::env::join_paths(paths) { + Ok(all) => all, + Err(_) => panic!("Couldn't join paths for PATH var."), + }; + + let mut process = match Command::new($crate::fs::executable_path()) + .env(NATIVE_PATH_ENV_VAR, paths_joined) + // .arg("--skip-plugins") + // .arg("--no-history") + // .arg("--config-file") + // .arg($crate::fs::DisplayPath::display_path(&$crate::fs::fixtures().join("playground/config/default.toml"))) + .arg(format!("-c 'cd {}; {}'", $crate::fs::in_directory($cwd), $crate::fs::DisplayPath::display_path(&path))) + .stdout(Stdio::piped()) + // .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(child) => child, + Err(why) => panic!("Can't run test {:?} {}", $crate::fs::executable_path(), why.to_string()), + }; + + // let stdin = process.stdin.as_mut().expect("couldn't open stdin"); + // stdin + // .write_all(b"exit\n") + // .expect("couldn't write to stdin"); + + let output = process + .wait_with_output() + .expect("couldn't read from stdout/stderr"); + + let out = $crate::macros::read_std(&output.stdout); + let err = String::from_utf8_lossy(&output.stderr); + + println!("=== stderr\n{}", err); + + $crate::Outcome::new(out,err.into_owned()) + }}; +} + +#[macro_export] +macro_rules! nu_with_plugins { + (cwd: $cwd:expr, $path:expr, $($part:expr),*) => {{ + use $crate::fs::DisplayPath; + + let path = format!($path, $( + $part.display_path() + ),*); + + nu_with_plugins!($cwd, &path) + }}; + + (cwd: $cwd:expr, $path:expr) => {{ + nu_with_plugins!($cwd, $path) + }}; + + ($cwd:expr, $path:expr) => {{ + pub use std::error::Error; + pub use std::io::prelude::*; + pub use std::process::{Command, Stdio}; + pub use crate::NATIVE_PATH_ENV_VAR; + + let commands = &*format!( + " + cd \"{}\" + {} + exit", + $crate::fs::in_directory($cwd), + $crate::fs::DisplayPath::display_path(&$path) + ); + + let test_bins = $crate::fs::binaries(); + let test_bins = nu_path::canonicalize(&test_bins).unwrap_or_else(|e| { + panic!( + "Couldn't canonicalize dummy binaries path {}: {:?}", + test_bins.display(), + e + ) + }); + + let mut paths = $crate::shell_os_paths(); + paths.insert(0, test_bins); + + let paths_joined = match std::env::join_paths(paths) { + Ok(all) => all, + Err(_) => panic!("Couldn't join paths for PATH var."), + }; + + let mut process = match Command::new($crate::fs::executable_path()) + .env(NATIVE_PATH_ENV_VAR, paths_joined) + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(child) => child, + Err(why) => panic!("Can't run test {}", why.to_string()), + }; + + let stdin = process.stdin.as_mut().expect("couldn't open stdin"); + stdin + .write_all(commands.as_bytes()) + .expect("couldn't write to stdin"); + + let output = process + .wait_with_output() + .expect("couldn't read from stdout/stderr"); + + let out = $crate::macros::read_std(&output.stdout); + let err = String::from_utf8_lossy(&output.stderr); + + println!("=== stderr\n{}", err); + + $crate::Outcome::new(out,err.into_owned()) + }}; +} + +pub fn read_std(std: &[u8]) -> String { + let out = String::from_utf8_lossy(std); + let out = out.lines().collect::>().join("\n"); + let out = out.replace("\r\n", ""); + out.replace("\n", "") +} diff --git a/crates/nu-test-support/src/playground.rs b/crates/nu-test-support/src/playground.rs new file mode 100644 index 0000000000..caeb4f26cf --- /dev/null +++ b/crates/nu-test-support/src/playground.rs @@ -0,0 +1,12 @@ +mod director; +pub mod matchers; +pub mod nu_process; +mod play; + +#[cfg(test)] +mod tests; + +pub use director::Director; +pub use matchers::says; +pub use nu_process::{Executable, NuProcess, NuResult, Outcome}; +pub use play::{Dirs, EnvironmentVariable, Playground}; diff --git a/crates/nu-test-support/src/playground/director.rs b/crates/nu-test-support/src/playground/director.rs new file mode 100644 index 0000000000..ba8b9922f4 --- /dev/null +++ b/crates/nu-test-support/src/playground/director.rs @@ -0,0 +1,163 @@ +use super::nu_process::*; +use super::EnvironmentVariable; +use std::ffi::OsString; +use std::fmt; + +#[derive(Default, Debug)] +pub struct Director { + pub cwd: Option, + pub environment_vars: Vec, + pub config: Option, + pub pipeline: Option>, + pub executable: Option, +} + +impl Director { + pub fn cococo(&self, arg: &str) -> Self { + let mut process = NuProcess { + environment_vars: self.environment_vars.clone(), + ..Default::default() + }; + + process.args(&["--testbin", "cococo", arg]); + Director { + config: self.config.clone(), + executable: Some(process), + environment_vars: self.environment_vars.clone(), + ..Default::default() + } + } + + pub fn and_then(&mut self, commands: &str) -> &mut Self { + let commands = commands.to_string(); + + if let Some(ref mut pipeline) = self.pipeline { + pipeline.push(commands); + } else { + self.pipeline = Some(vec![commands]); + } + + self + } + + pub fn pipeline(&self, commands: &str) -> Self { + let mut director = Director { + pipeline: if commands.is_empty() { + None + } else { + Some(vec![commands.to_string()]) + }, + ..Default::default() + }; + + let mut process = NuProcess { + environment_vars: self.environment_vars.clone(), + ..Default::default() + }; + + if let Some(working_directory) = &self.cwd { + process.cwd(working_directory); + } + + process.arg("--skip-plugins"); + process.arg("--no-history"); + if let Some(config_file) = self.config.as_ref() { + process.args(&[ + "--config-file", + config_file.to_str().expect("failed to convert."), + ]); + } + process.arg("--perf"); + + director.executable = Some(process); + director + } + + pub fn executable(&self) -> Option<&NuProcess> { + if let Some(binary) = &self.executable { + Some(binary) + } else { + None + } + } +} + +impl Executable for Director { + fn execute(&mut self) -> NuResult { + use std::io::Write; + use std::process::Stdio; + + match self.executable() { + Some(binary) => { + let mut process = match binary + .construct() + .stdout(Stdio::piped()) + .stdin(Stdio::piped()) + .stderr(Stdio::piped()) + .spawn() + { + Ok(child) => child, + Err(why) => panic!("Can't run test {}", why), + }; + + if let Some(pipelines) = &self.pipeline { + let child = process.stdin.as_mut().expect("Failed to open stdin"); + + for pipeline in pipelines { + child + .write_all(format!("{}\n", pipeline).as_bytes()) + .expect("Could not write to"); + } + + child.write_all(b"exit\n").expect("Could not write to"); + } + + process + .wait_with_output() + .map_err(|_| { + let reason = format!( + "could not execute process {} ({})", + binary, "No execution took place" + ); + + NuError { + desc: reason, + exit: None, + output: None, + } + }) + .and_then(|process| { + let out = + Outcome::new(&read_std(&process.stdout), &read_std(&process.stderr)); + + match process.status.success() { + true => Ok(out), + false => Err(NuError { + desc: String::new(), + exit: Some(process.status), + output: Some(out), + }), + } + }) + } + None => Err(NuError { + desc: String::from("err"), + exit: None, + output: None, + }), + } + } +} + +impl fmt::Display for Director { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "director") + } +} + +fn read_std(std: &[u8]) -> Vec { + let out = String::from_utf8_lossy(std); + let out = out.lines().collect::>().join("\n"); + let out = out.replace("\r\n", ""); + out.replace("\n", "").into_bytes() +} diff --git a/crates/nu-test-support/src/playground/matchers.rs b/crates/nu-test-support/src/playground/matchers.rs new file mode 100644 index 0000000000..7c36489e2d --- /dev/null +++ b/crates/nu-test-support/src/playground/matchers.rs @@ -0,0 +1,105 @@ +use hamcrest2::core::{MatchResult, Matcher}; +use std::fmt; +use std::str; + +use super::nu_process::Outcome; +use super::{Director, Executable}; + +#[derive(Clone)] +pub struct Play { + stdout_expectation: Option, +} + +impl fmt::Display for Play { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "play") + } +} + +impl fmt::Debug for Play { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "play") + } +} + +pub fn says() -> Play { + Play { + stdout_expectation: None, + } +} + +trait CheckerMatchers { + fn output(&self, actual: &Outcome) -> MatchResult; + fn std(&self, actual: &[u8], expected: Option<&String>, description: &str) -> MatchResult; + fn stdout(&self, actual: &Outcome) -> MatchResult; +} + +impl CheckerMatchers for Play { + fn output(&self, actual: &Outcome) -> MatchResult { + self.stdout(actual) + } + + fn stdout(&self, actual: &Outcome) -> MatchResult { + self.std(&actual.out, self.stdout_expectation.as_ref(), "stdout") + } + + fn std(&self, actual: &[u8], expected: Option<&String>, description: &str) -> MatchResult { + let out = match expected { + Some(out) => out, + None => return Ok(()), + }; + let actual = match str::from_utf8(actual) { + Err(..) => return Err(format!("{} was not utf8 encoded", description)), + Ok(actual) => actual, + }; + + if actual != *out { + return Err(format!( + "not equal:\n actual: {}\n expected: {}\n\n", + actual, out + )); + } + + Ok(()) + } +} + +impl Matcher for Play { + fn matches(&self, output: Outcome) -> MatchResult { + self.output(&output) + } +} + +impl Matcher for Play { + fn matches(&self, mut director: Director) -> MatchResult { + self.matches(&mut director) + } +} + +impl<'a> Matcher<&'a mut Director> for Play { + fn matches(&self, director: &'a mut Director) -> MatchResult { + if director.executable().is_none() { + return Err(format!("no such process {}", director)); + } + + let res = director.execute(); + + match res { + Ok(out) => self.output(&out), + Err(err) => { + if let Some(out) = &err.output { + return self.output(out); + } + + Err(format!("could not exec process {}: {:?}", director, err)) + } + } + } +} + +impl Play { + pub fn stdout(mut self, expected: &str) -> Self { + self.stdout_expectation = Some(expected.to_string()); + self + } +} diff --git a/crates/nu-test-support/src/playground/nu_process.rs b/crates/nu-test-support/src/playground/nu_process.rs new file mode 100644 index 0000000000..ffb3271ac4 --- /dev/null +++ b/crates/nu-test-support/src/playground/nu_process.rs @@ -0,0 +1,104 @@ +use super::EnvironmentVariable; +use crate::fs::{binaries as test_bins_path, executable_path}; +use std::ffi::{OsStr, OsString}; +use std::fmt; +use std::path::Path; +use std::process::{Command, ExitStatus}; + +pub trait Executable { + fn execute(&mut self) -> NuResult; +} + +#[derive(Clone, Debug)] +pub struct Outcome { + pub out: Vec, + pub err: Vec, +} + +impl Outcome { + pub fn new(out: &[u8], err: &[u8]) -> Outcome { + Outcome { + out: out.to_vec(), + err: err.to_vec(), + } + } +} + +pub type NuResult = Result; + +#[derive(Debug)] +pub struct NuError { + pub desc: String, + pub exit: Option, + pub output: Option, +} + +#[derive(Clone, Debug, Default)] +pub struct NuProcess { + pub arguments: Vec, + pub environment_vars: Vec, + pub cwd: Option, +} + +impl fmt::Display for NuProcess { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!(f, "`nu")?; + + for arg in &self.arguments { + write!(f, " {}", arg.to_string_lossy())?; + } + + write!(f, "`") + } +} + +impl NuProcess { + pub fn arg>(&mut self, arg: T) -> &mut Self { + self.arguments.push(arg.as_ref().to_os_string()); + self + } + + pub fn args>(&mut self, arguments: &[T]) -> &mut NuProcess { + self.arguments + .extend(arguments.iter().map(|t| t.as_ref().to_os_string())); + self + } + + pub fn cwd>(&mut self, path: T) -> &mut NuProcess { + self.cwd = Some(path.as_ref().to_os_string()); + self + } + + pub fn get_cwd(&self) -> Option<&Path> { + self.cwd.as_ref().map(Path::new) + } + + pub fn construct(&self) -> Command { + let mut command = Command::new(&executable_path()); + + if let Some(cwd) = self.get_cwd() { + command.current_dir(cwd); + } + + command.env_clear(); + + let paths = vec![test_bins_path()]; + + let paths_joined = match std::env::join_paths(&paths) { + Ok(all) => all, + Err(_) => panic!("Couldn't join paths for PATH var."), + }; + + command.env(crate::NATIVE_PATH_ENV_VAR, paths_joined); + + for env_var in &self.environment_vars { + command.env(&env_var.name, &env_var.value); + } + + for arg in &self.arguments { + command.arg(arg); + } + + command + } +} diff --git a/crates/nu-test-support/src/playground/play.rs b/crates/nu-test-support/src/playground/play.rs new file mode 100644 index 0000000000..2129352913 --- /dev/null +++ b/crates/nu-test-support/src/playground/play.rs @@ -0,0 +1,248 @@ +use super::Director; +use crate::fs; +use crate::fs::Stub; +use getset::Getters; +use glob::glob; +use std::path::{Path, PathBuf}; +use std::str; +use tempfile::{tempdir, TempDir}; + +#[derive(Default, Clone, Debug)] +pub struct EnvironmentVariable { + pub name: String, + pub value: String, +} + +impl EnvironmentVariable { + fn new(name: &str, value: &str) -> Self { + Self { + name: name.to_string(), + value: value.to_string(), + } + } +} + +pub struct Playground<'a> { + root: TempDir, + tests: String, + cwd: PathBuf, + config: PathBuf, + environment_vars: Vec, + dirs: &'a Dirs, +} + +#[derive(Default, Getters, Clone)] +#[get = "pub"] +pub struct Dirs { + pub root: PathBuf, + pub test: PathBuf, + pub fixtures: PathBuf, +} + +impl Dirs { + pub fn formats(&self) -> PathBuf { + self.fixtures.join("formats") + } + + pub fn config_fixtures(&self) -> PathBuf { + self.fixtures.join("playground/config") + } +} + +impl<'a> Playground<'a> { + pub fn root(&self) -> &Path { + self.root.path() + } + + pub fn cwd(&self) -> &Path { + &self.cwd + } + + pub fn back_to_playground(&mut self) -> &mut Self { + self.cwd = PathBuf::from(self.root()).join(self.tests.clone()); + self + } + + pub fn play(&mut self) -> &mut Self { + self + } + + pub fn setup(topic: &str, block: impl FnOnce(Dirs, &mut Playground)) { + let root = tempdir().expect("Couldn't create a tempdir"); + let nuplay_dir = root.path().join(topic); + + if PathBuf::from(&nuplay_dir).exists() { + std::fs::remove_dir_all(PathBuf::from(&nuplay_dir)).expect("can not remove directory"); + } + + std::fs::create_dir(PathBuf::from(&nuplay_dir)).expect("can not create directory"); + + let fixtures = fs::fixtures(); + let cwd = std::env::current_dir().expect("Could not get current working directory."); + let fixtures = nu_path::canonicalize_with(fixtures.clone(), cwd).unwrap_or_else(|e| { + panic!( + "Couldn't canonicalize fixtures path {}: {:?}", + fixtures.display(), + e + ) + }); + + let mut playground = Playground { + root, + tests: topic.to_string(), + cwd: nuplay_dir, + config: fixtures.join("playground/config/default.toml"), + environment_vars: Vec::default(), + dirs: &Dirs::default(), + }; + + let playground_root = playground.root.path(); + + let cwd = std::env::current_dir().expect("Could not get current working directory."); + let test = + nu_path::canonicalize_with(playground_root.join(topic), cwd).unwrap_or_else(|e| { + panic!( + "Couldn't canonicalize test path {}: {:?}", + playground_root.join(topic).display(), + e + ) + }); + + let cwd = std::env::current_dir().expect("Could not get current working directory."); + let root = nu_path::canonicalize_with(playground_root, cwd).unwrap_or_else(|e| { + panic!( + "Couldn't canonicalize tests root path {}: {:?}", + playground_root.display(), + e + ) + }); + + let dirs = Dirs { + root, + test, + fixtures, + }; + + playground.dirs = &dirs; + + block(dirs.clone(), &mut playground); + } + + pub fn with_config(&mut self, source_file: impl AsRef) -> &mut Self { + self.config = source_file.as_ref().to_path_buf(); + self + } + + pub fn with_env(&mut self, name: &str, value: &str) -> &mut Self { + self.environment_vars + .push(EnvironmentVariable::new(name, value)); + self + } + + pub fn get_config(&self) -> &str { + self.config.to_str().expect("could not convert path.") + } + + pub fn build(&mut self) -> Director { + Director { + cwd: Some(self.dirs.test().into()), + config: Some(self.config.clone().into()), + environment_vars: self.environment_vars.clone(), + ..Default::default() + } + } + + pub fn cococo(&mut self, arg: &str) -> Director { + self.build().cococo(arg) + } + + pub fn pipeline(&mut self, commands: &str) -> Director { + self.build().pipeline(commands) + } + + pub fn mkdir(&mut self, directory: &str) -> &mut Self { + self.cwd.push(directory); + std::fs::create_dir_all(&self.cwd).expect("can not create directory"); + self.back_to_playground(); + self + } + + #[cfg(not(target_arch = "wasm32"))] + pub fn symlink(&mut self, from: impl AsRef, to: impl AsRef) -> &mut Self { + let from = self.cwd.join(from); + let to = self.cwd.join(to); + + let create_symlink = { + #[cfg(unix)] + { + std::os::unix::fs::symlink + } + + #[cfg(windows)] + { + if from.is_file() { + std::os::windows::fs::symlink_file + } else if from.is_dir() { + std::os::windows::fs::symlink_dir + } else { + panic!("symlink from must be a file or dir") + } + } + }; + + create_symlink(from, to).expect("can not create symlink"); + self.back_to_playground(); + self + } + + pub fn with_files(&mut self, files: Vec) -> &mut Self { + let endl = fs::line_ending(); + + files + .iter() + .map(|f| { + let mut path = PathBuf::from(&self.cwd); + + let (file_name, contents) = match *f { + Stub::EmptyFile(name) => (name, "fake data".to_string()), + Stub::FileWithContent(name, content) => (name, content.to_string()), + Stub::FileWithContentToBeTrimmed(name, content) => ( + name, + content + .lines() + .skip(1) + .map(|line| line.trim()) + .collect::>() + .join(&endl), + ), + }; + + path.push(file_name); + + std::fs::write(path, contents.as_bytes()).expect("can not create file"); + }) + .for_each(drop); + self.back_to_playground(); + self + } + + pub fn within(&mut self, directory: &str) -> &mut Self { + self.cwd.push(directory); + std::fs::create_dir(&self.cwd).expect("can not create directory"); + self + } + + pub fn glob_vec(pattern: &str) -> Vec { + let glob = glob(pattern); + + glob.expect("invalid pattern") + .map(|path| { + if let Ok(path) = path { + path + } else { + unreachable!() + } + }) + .collect() + } +} diff --git a/crates/nu-test-support/src/playground/tests.rs b/crates/nu-test-support/src/playground/tests.rs new file mode 100644 index 0000000000..014a86910e --- /dev/null +++ b/crates/nu-test-support/src/playground/tests.rs @@ -0,0 +1,41 @@ +use crate::playground::Playground; +use std::path::{Path, PathBuf}; + +use super::matchers::says; +use hamcrest2::assert_that; +use hamcrest2::prelude::*; + +fn path(p: &Path) -> PathBuf { + let cwd = std::env::current_dir().expect("Could not get current working directory."); + nu_path::canonicalize_with(p, cwd) + .unwrap_or_else(|e| panic!("Couldn't canonicalize path {}: {:?}", p.display(), e)) +} + +#[test] +fn asserts_standard_out_expectation_from_nu_executable() { + Playground::setup("topic", |_, nu| { + assert_that!(nu.cococo("andres"), says().stdout("andres")); + }) +} + +#[test] +fn current_working_directory_in_sandbox_directory_created() { + Playground::setup("topic", |dirs, nu| { + let original_cwd = dirs.test(); + nu.within("some_directory_within"); + + assert_eq!(path(nu.cwd()), original_cwd.join("some_directory_within")); + }) +} + +#[test] +fn current_working_directory_back_to_root_from_anywhere() { + Playground::setup("topic", |dirs, nu| { + let original_cwd = dirs.test(); + + nu.within("some_directory_within"); + nu.back_to_playground(); + + assert_eq!(path(nu.cwd()), *original_cwd); + }) +} diff --git a/src/config_files.rs b/src/config_files.rs index 727cbed376..ea053051fa 100644 --- a/src/config_files.rs +++ b/src/config_files.rs @@ -39,7 +39,7 @@ pub(crate) fn read_config_file(engine_state: &mut EngineState, stack: &mut Stack if config_path.exists() { // FIXME: remove this message when we're ready - println!("Loading config from: {:?}", config_path); + //println!("Loading config from: {:?}", config_path); let config_filename = config_path.to_string_lossy().to_owned(); if let Ok(contents) = std::fs::read_to_string(&config_path) { diff --git a/src/main.rs b/src/main.rs index 8363b8e379..0a3cd51872 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,8 @@ mod utils; #[cfg(test)] mod tests; +mod test_bins; + use miette::Result; use nu_command::{create_default_context, BufferedReader}; use nu_engine::get_full_help; @@ -21,7 +23,7 @@ use nu_protocol::{ Spanned, SyntaxShape, Value, CONFIG_VARIABLE_ID, }; use std::{ - io::BufReader, + io::{BufReader, Write}, path::Path, sync::{ atomic::{AtomicBool, Ordering}, @@ -92,6 +94,7 @@ fn main() -> Result<()> { // Cool, it's a flag if arg == "-c" || arg == "--commands" + || arg == "--testbin" || arg == "--develop" || arg == "--debug" || arg == "--loglevel" @@ -115,6 +118,21 @@ fn main() -> Result<()> { match nushell_config { Ok(nushell_config) => { + if let Some(testbin) = &nushell_config.testbin { + // Call out to the correct testbin + match testbin.item.as_str() { + "echo_env" => test_bins::echo_env(), + "cococo" => test_bins::cococo(), + "meow" => test_bins::meow(), + "iecho" => test_bins::iecho(), + "fail" => test_bins::fail(), + "nonu" => test_bins::nonu(), + "chop" => test_bins::chop(), + "repeater" => test_bins::repeater(), + _ => std::process::exit(1), + } + std::process::exit(0) + } let input = if let Some(redirect_stdin) = &nushell_config.redirect_stdin { let stdin = std::io::stdin(); let buf_reader = BufReader::new(stdin); @@ -193,6 +211,7 @@ fn parse_commandline_args( let login_shell = call.get_named_arg("login"); let interactive_shell = call.get_named_arg("interactive"); let commands: Option = call.get_flag_expr("commands"); + let testbin: Option = call.get_flag_expr("testbin"); let commands = if let Some(expression) = commands { let contents = engine_state.get_span_contents(&expression.span); @@ -205,12 +224,29 @@ fn parse_commandline_args( None }; + let testbin = if let Some(expression) = testbin { + let contents = engine_state.get_span_contents(&expression.span); + + Some(Spanned { + item: String::from_utf8_lossy(contents).to_string(), + span: expression.span, + }) + } else { + None + }; + let help = call.has_flag("help"); if help { let full_help = get_full_help(&Nu.signature(), &Nu.examples(), engine_state, &mut stack); - print!("{}", full_help); + + let _ = std::panic::catch_unwind(move || { + let stdout = std::io::stdout(); + let mut stdout = stdout.lock(); + let _ = stdout.write_all(full_help.as_bytes()); + }); + std::process::exit(1); } @@ -219,6 +255,7 @@ fn parse_commandline_args( login_shell, interactive_shell, commands, + testbin, }); } } @@ -235,6 +272,7 @@ struct NushellConfig { login_shell: Option>, interactive_shell: Option>, commands: Option>, + testbin: Option>, } #[derive(Clone)] @@ -251,6 +289,12 @@ impl Command for Nu { .switch("stdin", "redirect the stdin", None) .switch("login", "start as a login shell", Some('l')) .switch("interactive", "start as an interactive shell", Some('i')) + .named( + "testbin", + SyntaxShape::String, + "run internal test binary", + None, + ) .named( "commands", SyntaxShape::String, diff --git a/src/test_bins.rs b/src/test_bins.rs new file mode 100644 index 0000000000..b02f00e09f --- /dev/null +++ b/src/test_bins.rs @@ -0,0 +1,123 @@ +use std::io::{self, BufRead, Write}; + +/// Echo's value of env keys from args +/// Example: nu --testbin env_echo FOO BAR +/// If it it's not present echo's nothing +pub fn echo_env() { + let args = args(); + for arg in args { + if let Ok(v) = std::env::var(arg) { + println!("{}", v); + } + } +} + +pub fn cococo() { + let args: Vec = args(); + + if args.len() > 1 { + // Write back out all the arguments passed + // if given at least 1 instead of chickens + // speaking co co co. + println!("{}", &args[1..].join(" ")); + } else { + println!("cococo"); + } +} + +pub fn meow() { + let args: Vec = args(); + + for arg in args.iter().skip(1) { + let contents = std::fs::read_to_string(arg).expect("Expected a filepath"); + println!("{}", contents); + } +} + +pub fn nonu() { + args().iter().skip(1).for_each(|arg| print!("{}", arg)); +} + +pub fn repeater() { + let mut stdout = io::stdout(); + let args = args(); + let mut args = args.iter().skip(1); + let letter = args.next().expect("needs a character to iterate"); + let count = args.next().expect("need the number of times to iterate"); + + let count: u64 = count.parse().expect("can't convert count to number"); + + for _ in 0..count { + let _ = write!(stdout, "{}", letter); + } + let _ = stdout.flush(); +} + +pub fn iecho() { + // println! panics if stdout gets closed, whereas writeln gives us an error + let mut stdout = io::stdout(); + let _ = args() + .iter() + .skip(1) + .cycle() + .try_for_each(|v| writeln!(stdout, "{}", v)); +} + +pub fn fail() { + std::process::exit(1); +} + +pub fn chop() { + if did_chop_arguments() { + // we are done and don't care about standard input. + std::process::exit(0); + } + + // if no arguments given, chop from standard input and exit. + let stdin = io::stdin(); + let mut stdout = io::stdout(); + + for given in stdin.lock().lines().flatten() { + let chopped = if given.is_empty() { + &given + } else { + let to = given.len() - 1; + &given[..to] + }; + + if let Err(_e) = writeln!(stdout, "{}", chopped) { + break; + } + } + + std::process::exit(0); +} + +fn did_chop_arguments() -> bool { + let args: Vec = args(); + + if args.len() > 1 { + let mut arguments = args.iter(); + arguments.next(); + + for arg in arguments { + let chopped = if arg.is_empty() { + &arg + } else { + let to = arg.len() - 1; + &arg[..to] + }; + + println!("{}", chopped); + } + + return true; + } + + false +} + +fn args() -> Vec { + // skip (--testbin bin_name args) + std::env::args().skip(2).collect() +} diff --git a/src/tests.rs b/src/tests.rs index 3a422fab48..a41fed6ea9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -26,7 +26,7 @@ pub fn run_test(input: &str, expected: &str) -> TestResult { let mut file = NamedTempFile::new()?; let name = file.path(); - let mut cmd = Command::cargo_bin("engine-q")?; + let mut cmd = Command::cargo_bin("nu")?; cmd.arg(name); writeln!(file, "{}", input)?; @@ -51,7 +51,7 @@ pub fn run_test_contains(input: &str, expected: &str) -> TestResult { let mut file = NamedTempFile::new()?; let name = file.path(); - let mut cmd = Command::cargo_bin("engine-q")?; + let mut cmd = Command::cargo_bin("nu")?; cmd.arg(name); writeln!(file, "{}", input)?; @@ -76,7 +76,7 @@ pub fn fail_test(input: &str, expected: &str) -> TestResult { let mut file = NamedTempFile::new()?; let name = file.path(); - let mut cmd = Command::cargo_bin("engine-q")?; + let mut cmd = Command::cargo_bin("nu")?; cmd.arg(name); cmd.env( "PWD", diff --git a/tests/assets/nu_json/charset_result.hjson b/tests/assets/nu_json/charset_result.hjson new file mode 100644 index 0000000000..d1573b6162 --- /dev/null +++ b/tests/assets/nu_json/charset_result.hjson @@ -0,0 +1,5 @@ +{ + ql-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + js-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + ml-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +} \ No newline at end of file diff --git a/tests/assets/nu_json/charset_result.json b/tests/assets/nu_json/charset_result.json new file mode 100644 index 0000000000..6357d96db6 --- /dev/null +++ b/tests/assets/nu_json/charset_result.json @@ -0,0 +1,5 @@ +{ + "ql-ascii": "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + "js-ascii": "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~", + "ml-ascii": "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +} \ No newline at end of file diff --git a/tests/assets/nu_json/charset_test.hjson b/tests/assets/nu_json/charset_test.hjson new file mode 100644 index 0000000000..7527b1e45b --- /dev/null +++ b/tests/assets/nu_json/charset_test.hjson @@ -0,0 +1,6 @@ +ql-ascii: ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ +js-ascii: "! \"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~" +ml-ascii: + ''' + ! "#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~ + ''' diff --git a/tests/assets/nu_json/comments_result.hjson b/tests/assets/nu_json/comments_result.hjson new file mode 100644 index 0000000000..a99ce23dba --- /dev/null +++ b/tests/assets/nu_json/comments_result.hjson @@ -0,0 +1,26 @@ +{ + foo1: This is a string value. # part of the string + foo2: This is a string value. + bar1: This is a string value. // part of the string + bar2: This is a string value. + foobar1: This is a string value./* part of the string */ + foobar2: This is a string value. + rem1: "# test" + rem2: "// test" + rem3: "/* test */" + num1: 0 + num2: 0 + num3: 2 + true1: true + true2: true + true3: true + false1: false + false2: false + false3: false + null1: null + null2: null + null3: null + str1: 00 # part of the string + str2: 00.0 // part of the string + str3: 02 /* part of the string */ +} \ No newline at end of file diff --git a/tests/assets/nu_json/comments_result.json b/tests/assets/nu_json/comments_result.json new file mode 100644 index 0000000000..e247803e65 --- /dev/null +++ b/tests/assets/nu_json/comments_result.json @@ -0,0 +1,26 @@ +{ + "foo1": "This is a string value. # part of the string", + "foo2": "This is a string value.", + "bar1": "This is a string value. // part of the string", + "bar2": "This is a string value.", + "foobar1": "This is a string value./* part of the string */", + "foobar2": "This is a string value.", + "rem1": "# test", + "rem2": "// test", + "rem3": "/* test */", + "num1": 0, + "num2": 0, + "num3": 2, + "true1": true, + "true2": true, + "true3": true, + "false1": false, + "false2": false, + "false3": false, + "null1": null, + "null2": null, + "null3": null, + "str1": "00 # part of the string", + "str2": "00.0 // part of the string", + "str3": "02 /* part of the string */" +} \ No newline at end of file diff --git a/tests/assets/nu_json/comments_test.hjson b/tests/assets/nu_json/comments_test.hjson new file mode 100644 index 0000000000..7f1dfdcab6 --- /dev/null +++ b/tests/assets/nu_json/comments_test.hjson @@ -0,0 +1,48 @@ +// test +# all +// comment +/* +styles +*/ +# with lf + + + +# ! + +{ + # hjson style comment + foo1: This is a string value. # part of the string + foo2: "This is a string value." # a comment + + // js style comment + bar1: This is a string value. // part of the string + bar2: "This is a string value." // a comment + + /* js block style comments */foobar1:/* more */This is a string value./* part of the string */ + /* js block style comments */foobar2:/* more */"This is a string value."/* a comment */ + + rem1: "# test" + rem2: "// test" + rem3: "/* test */" + + num1: 0 # comment + num2: 0.0 // comment + num3: 2 /* comment */ + + true1: true # comment + true2: true // comment + true3: true /* comment */ + + false1: false # comment + false2: false // comment + false3: false /* comment */ + + null1: null # comment + null2: null // comment + null3: null /* comment */ + + str1: 00 # part of the string + str2: 00.0 // part of the string + str3: 02 /* part of the string */ +} diff --git a/tests/assets/nu_json/empty_result.hjson b/tests/assets/nu_json/empty_result.hjson new file mode 100644 index 0000000000..a75b45b28b --- /dev/null +++ b/tests/assets/nu_json/empty_result.hjson @@ -0,0 +1,3 @@ +{ + "": empty +} \ No newline at end of file diff --git a/tests/assets/nu_json/empty_result.json b/tests/assets/nu_json/empty_result.json new file mode 100644 index 0000000000..47f710fe23 --- /dev/null +++ b/tests/assets/nu_json/empty_result.json @@ -0,0 +1,3 @@ +{ + "": "empty" +} \ No newline at end of file diff --git a/tests/assets/nu_json/empty_test.hjson b/tests/assets/nu_json/empty_test.hjson new file mode 100644 index 0000000000..ac97a9d7ce --- /dev/null +++ b/tests/assets/nu_json/empty_test.hjson @@ -0,0 +1,3 @@ +{ + "": empty +} diff --git a/tests/assets/nu_json/failCharset1_test.hjson b/tests/assets/nu_json/failCharset1_test.hjson new file mode 100644 index 0000000000..da280393b2 --- /dev/null +++ b/tests/assets/nu_json/failCharset1_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid \u char + char: "\uxxxx" +} diff --git a/tests/assets/nu_json/failJSON02_test.json b/tests/assets/nu_json/failJSON02_test.json new file mode 100644 index 0000000000..6b7c11e5a5 --- /dev/null +++ b/tests/assets/nu_json/failJSON02_test.json @@ -0,0 +1 @@ +["Unclosed array" \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON05_test.json b/tests/assets/nu_json/failJSON05_test.json new file mode 100644 index 0000000000..ddf3ce3d24 --- /dev/null +++ b/tests/assets/nu_json/failJSON05_test.json @@ -0,0 +1 @@ +["double extra comma",,] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON06_test.json b/tests/assets/nu_json/failJSON06_test.json new file mode 100644 index 0000000000..ed91580e1b --- /dev/null +++ b/tests/assets/nu_json/failJSON06_test.json @@ -0,0 +1 @@ +[ , "<-- missing value"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON07_test.json b/tests/assets/nu_json/failJSON07_test.json new file mode 100644 index 0000000000..8a96af3e4e --- /dev/null +++ b/tests/assets/nu_json/failJSON07_test.json @@ -0,0 +1 @@ +["Comma after the close"], \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON08_test.json b/tests/assets/nu_json/failJSON08_test.json new file mode 100644 index 0000000000..b28479c6ec --- /dev/null +++ b/tests/assets/nu_json/failJSON08_test.json @@ -0,0 +1 @@ +["Extra close"]] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON10_test.json b/tests/assets/nu_json/failJSON10_test.json new file mode 100644 index 0000000000..5d8c0047bd --- /dev/null +++ b/tests/assets/nu_json/failJSON10_test.json @@ -0,0 +1 @@ +{"Extra value after close": true} "misplaced quoted value" \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON11_test.json b/tests/assets/nu_json/failJSON11_test.json new file mode 100644 index 0000000000..76eb95b458 --- /dev/null +++ b/tests/assets/nu_json/failJSON11_test.json @@ -0,0 +1 @@ +{"Illegal expression": 1 + 2} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON12_test.json b/tests/assets/nu_json/failJSON12_test.json new file mode 100644 index 0000000000..77580a4522 --- /dev/null +++ b/tests/assets/nu_json/failJSON12_test.json @@ -0,0 +1 @@ +{"Illegal invocation": alert()} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON13_test.json b/tests/assets/nu_json/failJSON13_test.json new file mode 100644 index 0000000000..379406b59b --- /dev/null +++ b/tests/assets/nu_json/failJSON13_test.json @@ -0,0 +1 @@ +{"Numbers cannot have leading zeroes": 013} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON14_test.json b/tests/assets/nu_json/failJSON14_test.json new file mode 100644 index 0000000000..0ed366b38a --- /dev/null +++ b/tests/assets/nu_json/failJSON14_test.json @@ -0,0 +1 @@ +{"Numbers cannot be hex": 0x14} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON15_test.json b/tests/assets/nu_json/failJSON15_test.json new file mode 100644 index 0000000000..fc8376b605 --- /dev/null +++ b/tests/assets/nu_json/failJSON15_test.json @@ -0,0 +1 @@ +["Illegal backslash escape: \x15"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON16_test.json b/tests/assets/nu_json/failJSON16_test.json new file mode 100644 index 0000000000..3fe21d4b53 --- /dev/null +++ b/tests/assets/nu_json/failJSON16_test.json @@ -0,0 +1 @@ +[\naked] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON17_test.json b/tests/assets/nu_json/failJSON17_test.json new file mode 100644 index 0000000000..62b9214aed --- /dev/null +++ b/tests/assets/nu_json/failJSON17_test.json @@ -0,0 +1 @@ +["Illegal backslash escape: \017"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON19_test.json b/tests/assets/nu_json/failJSON19_test.json new file mode 100644 index 0000000000..3b9c46fa9a --- /dev/null +++ b/tests/assets/nu_json/failJSON19_test.json @@ -0,0 +1 @@ +{"Missing colon" null} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON20_test.json b/tests/assets/nu_json/failJSON20_test.json new file mode 100644 index 0000000000..27c1af3e72 --- /dev/null +++ b/tests/assets/nu_json/failJSON20_test.json @@ -0,0 +1 @@ +{"Double colon":: null} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON21_test.json b/tests/assets/nu_json/failJSON21_test.json new file mode 100644 index 0000000000..62474573b2 --- /dev/null +++ b/tests/assets/nu_json/failJSON21_test.json @@ -0,0 +1 @@ +{"Comma instead of colon", null} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON22_test.json b/tests/assets/nu_json/failJSON22_test.json new file mode 100644 index 0000000000..a7752581bc --- /dev/null +++ b/tests/assets/nu_json/failJSON22_test.json @@ -0,0 +1 @@ +["Colon instead of comma": false] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON23_test.json b/tests/assets/nu_json/failJSON23_test.json new file mode 100644 index 0000000000..494add1ca1 --- /dev/null +++ b/tests/assets/nu_json/failJSON23_test.json @@ -0,0 +1 @@ +["Bad value", truth] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON24_test.json b/tests/assets/nu_json/failJSON24_test.json new file mode 100644 index 0000000000..caff239bfc --- /dev/null +++ b/tests/assets/nu_json/failJSON24_test.json @@ -0,0 +1 @@ +['single quote'] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON26_test.json b/tests/assets/nu_json/failJSON26_test.json new file mode 100644 index 0000000000..845d26a6a5 --- /dev/null +++ b/tests/assets/nu_json/failJSON26_test.json @@ -0,0 +1 @@ +["tab\ character\ in\ string\ "] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON28_test.json b/tests/assets/nu_json/failJSON28_test.json new file mode 100644 index 0000000000..621a0101c6 --- /dev/null +++ b/tests/assets/nu_json/failJSON28_test.json @@ -0,0 +1,2 @@ +["line\ +break"] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON29_test.json b/tests/assets/nu_json/failJSON29_test.json new file mode 100644 index 0000000000..47ec421bb6 --- /dev/null +++ b/tests/assets/nu_json/failJSON29_test.json @@ -0,0 +1 @@ +[0e] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON30_test.json b/tests/assets/nu_json/failJSON30_test.json new file mode 100644 index 0000000000..8ab0bc4b8b --- /dev/null +++ b/tests/assets/nu_json/failJSON30_test.json @@ -0,0 +1 @@ +[0e+] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON31_test.json b/tests/assets/nu_json/failJSON31_test.json new file mode 100644 index 0000000000..1cce602b51 --- /dev/null +++ b/tests/assets/nu_json/failJSON31_test.json @@ -0,0 +1 @@ +[0e+-1] \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON32_test.json b/tests/assets/nu_json/failJSON32_test.json new file mode 100644 index 0000000000..45cba7396f --- /dev/null +++ b/tests/assets/nu_json/failJSON32_test.json @@ -0,0 +1 @@ +{"Comma instead if closing brace": true, \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON33_test.json b/tests/assets/nu_json/failJSON33_test.json new file mode 100644 index 0000000000..ca5eb19dc9 --- /dev/null +++ b/tests/assets/nu_json/failJSON33_test.json @@ -0,0 +1 @@ +["mismatch"} \ No newline at end of file diff --git a/tests/assets/nu_json/failJSON34_test.json b/tests/assets/nu_json/failJSON34_test.json new file mode 100644 index 0000000000..921436427e --- /dev/null +++ b/tests/assets/nu_json/failJSON34_test.json @@ -0,0 +1,2 @@ +A quoteless string is OK, +but two must be contained in an array. diff --git a/tests/assets/nu_json/failKey1_test.hjson b/tests/assets/nu_json/failKey1_test.hjson new file mode 100644 index 0000000000..0026d2a1ae --- /dev/null +++ b/tests/assets/nu_json/failKey1_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + wrong name: 0 +} diff --git a/tests/assets/nu_json/failKey2_test.hjson b/tests/assets/nu_json/failKey2_test.hjson new file mode 100644 index 0000000000..4b5771a7d2 --- /dev/null +++ b/tests/assets/nu_json/failKey2_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + {name: 0 +} diff --git a/tests/assets/nu_json/failKey3_test.hjson b/tests/assets/nu_json/failKey3_test.hjson new file mode 100644 index 0000000000..3443a87f4b --- /dev/null +++ b/tests/assets/nu_json/failKey3_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + key,name: 0 +} diff --git a/tests/assets/nu_json/failKey4_test.hjson b/tests/assets/nu_json/failKey4_test.hjson new file mode 100644 index 0000000000..a7e9ce2aea --- /dev/null +++ b/tests/assets/nu_json/failKey4_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid name + : 0 +} diff --git a/tests/assets/nu_json/failMLStr1_test.hjson b/tests/assets/nu_json/failMLStr1_test.hjson new file mode 100644 index 0000000000..ca6410720b --- /dev/null +++ b/tests/assets/nu_json/failMLStr1_test.hjson @@ -0,0 +1,3 @@ +{ + # invalid multiline string + ml: ''' diff --git a/tests/assets/nu_json/failObj1_test.hjson b/tests/assets/nu_json/failObj1_test.hjson new file mode 100644 index 0000000000..ac7b761fbf --- /dev/null +++ b/tests/assets/nu_json/failObj1_test.hjson @@ -0,0 +1,6 @@ +{ + # invalid obj + noDelimiter + { + } +} diff --git a/tests/assets/nu_json/failObj2_test.hjson b/tests/assets/nu_json/failObj2_test.hjson new file mode 100644 index 0000000000..cdad47eefe --- /dev/null +++ b/tests/assets/nu_json/failObj2_test.hjson @@ -0,0 +1,6 @@ +{ + # invalid obj + noEnd + { + +} diff --git a/tests/assets/nu_json/failObj3_test.hjson b/tests/assets/nu_json/failObj3_test.hjson new file mode 100644 index 0000000000..f1176842de --- /dev/null +++ b/tests/assets/nu_json/failObj3_test.hjson @@ -0,0 +1,7 @@ +{ + # missing key + + [ + test + ] +} diff --git a/tests/assets/nu_json/failStr1a_test.hjson b/tests/assets/nu_json/failStr1a_test.hjson new file mode 100644 index 0000000000..91b930ca44 --- /dev/null +++ b/tests/assets/nu_json/failStr1a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: ] +} diff --git a/tests/assets/nu_json/failStr1b_test.hjson b/tests/assets/nu_json/failStr1b_test.hjson new file mode 100644 index 0000000000..91da1a8dee --- /dev/null +++ b/tests/assets/nu_json/failStr1b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: ]x +} diff --git a/tests/assets/nu_json/failStr1c_test.hjson b/tests/assets/nu_json/failStr1c_test.hjson new file mode 100644 index 0000000000..20a73079f2 --- /dev/null +++ b/tests/assets/nu_json/failStr1c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + ] +] diff --git a/tests/assets/nu_json/failStr1d_test.hjson b/tests/assets/nu_json/failStr1d_test.hjson new file mode 100644 index 0000000000..555f88a915 --- /dev/null +++ b/tests/assets/nu_json/failStr1d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + ]x +] diff --git a/tests/assets/nu_json/failStr2a_test.hjson b/tests/assets/nu_json/failStr2a_test.hjson new file mode 100644 index 0000000000..5a8b28feb7 --- /dev/null +++ b/tests/assets/nu_json/failStr2a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: } +} diff --git a/tests/assets/nu_json/failStr2b_test.hjson b/tests/assets/nu_json/failStr2b_test.hjson new file mode 100644 index 0000000000..a7fc00de73 --- /dev/null +++ b/tests/assets/nu_json/failStr2b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: }x +} diff --git a/tests/assets/nu_json/failStr2c_test.hjson b/tests/assets/nu_json/failStr2c_test.hjson new file mode 100644 index 0000000000..1fe46067b6 --- /dev/null +++ b/tests/assets/nu_json/failStr2c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + } +] diff --git a/tests/assets/nu_json/failStr2d_test.hjson b/tests/assets/nu_json/failStr2d_test.hjson new file mode 100644 index 0000000000..ea8c99adcc --- /dev/null +++ b/tests/assets/nu_json/failStr2d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + }x +] diff --git a/tests/assets/nu_json/failStr3a_test.hjson b/tests/assets/nu_json/failStr3a_test.hjson new file mode 100644 index 0000000000..ec5d0ad209 --- /dev/null +++ b/tests/assets/nu_json/failStr3a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: { +} diff --git a/tests/assets/nu_json/failStr3b_test.hjson b/tests/assets/nu_json/failStr3b_test.hjson new file mode 100644 index 0000000000..2d0fff1fb9 --- /dev/null +++ b/tests/assets/nu_json/failStr3b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: {x +} diff --git a/tests/assets/nu_json/failStr3c_test.hjson b/tests/assets/nu_json/failStr3c_test.hjson new file mode 100644 index 0000000000..2872a4d95f --- /dev/null +++ b/tests/assets/nu_json/failStr3c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + { +] diff --git a/tests/assets/nu_json/failStr3d_test.hjson b/tests/assets/nu_json/failStr3d_test.hjson new file mode 100644 index 0000000000..949502ffc8 --- /dev/null +++ b/tests/assets/nu_json/failStr3d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + {x +] diff --git a/tests/assets/nu_json/failStr4a_test.hjson b/tests/assets/nu_json/failStr4a_test.hjson new file mode 100644 index 0000000000..941f35bcd7 --- /dev/null +++ b/tests/assets/nu_json/failStr4a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: [ +} diff --git a/tests/assets/nu_json/failStr4b_test.hjson b/tests/assets/nu_json/failStr4b_test.hjson new file mode 100644 index 0000000000..b7bb236281 --- /dev/null +++ b/tests/assets/nu_json/failStr4b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: [x +} diff --git a/tests/assets/nu_json/failStr4c_test.hjson b/tests/assets/nu_json/failStr4c_test.hjson new file mode 100644 index 0000000000..ee927a4757 --- /dev/null +++ b/tests/assets/nu_json/failStr4c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + [ +] diff --git a/tests/assets/nu_json/failStr4d_test.hjson b/tests/assets/nu_json/failStr4d_test.hjson new file mode 100644 index 0000000000..db50a529d8 --- /dev/null +++ b/tests/assets/nu_json/failStr4d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + [x +] diff --git a/tests/assets/nu_json/failStr5a_test.hjson b/tests/assets/nu_json/failStr5a_test.hjson new file mode 100644 index 0000000000..4093f7a58d --- /dev/null +++ b/tests/assets/nu_json/failStr5a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: : +} diff --git a/tests/assets/nu_json/failStr5b_test.hjson b/tests/assets/nu_json/failStr5b_test.hjson new file mode 100644 index 0000000000..eda96192ba --- /dev/null +++ b/tests/assets/nu_json/failStr5b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: :x +} diff --git a/tests/assets/nu_json/failStr5c_test.hjson b/tests/assets/nu_json/failStr5c_test.hjson new file mode 100644 index 0000000000..63280735b4 --- /dev/null +++ b/tests/assets/nu_json/failStr5c_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + : +] diff --git a/tests/assets/nu_json/failStr5d_test.hjson b/tests/assets/nu_json/failStr5d_test.hjson new file mode 100644 index 0000000000..bfaef8e7da --- /dev/null +++ b/tests/assets/nu_json/failStr5d_test.hjson @@ -0,0 +1,5 @@ +[ + foo + # invalid quoteless string + :x +] diff --git a/tests/assets/nu_json/failStr6a_test.hjson b/tests/assets/nu_json/failStr6a_test.hjson new file mode 100644 index 0000000000..522333773d --- /dev/null +++ b/tests/assets/nu_json/failStr6a_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: , +} diff --git a/tests/assets/nu_json/failStr6b_test.hjson b/tests/assets/nu_json/failStr6b_test.hjson new file mode 100644 index 0000000000..90ad67bf5a --- /dev/null +++ b/tests/assets/nu_json/failStr6b_test.hjson @@ -0,0 +1,4 @@ +{ + # invalid quoteless string + ql: ,x +} diff --git a/tests/assets/nu_json/failStr6c_test.hjson b/tests/assets/nu_json/failStr6c_test.hjson new file mode 100644 index 0000000000..81d2af4c9c --- /dev/null +++ b/tests/assets/nu_json/failStr6c_test.hjson @@ -0,0 +1,6 @@ +[ + # invalid quoteless string + # note that if there were a preceding value the comma would + # be allowed/ignored as a separator/trailing comma + , +] diff --git a/tests/assets/nu_json/failStr6d_test.hjson b/tests/assets/nu_json/failStr6d_test.hjson new file mode 100644 index 0000000000..c1477293b9 --- /dev/null +++ b/tests/assets/nu_json/failStr6d_test.hjson @@ -0,0 +1,6 @@ +[ + # invalid quoteless string + # note that if there were a preceding value the comma would + # be allowed/ignored as a separator/trailing comma + ,x +] diff --git a/tests/assets/nu_json/kan_result.hjson b/tests/assets/nu_json/kan_result.hjson new file mode 100644 index 0000000000..d2b839add0 --- /dev/null +++ b/tests/assets/nu_json/kan_result.hjson @@ -0,0 +1,48 @@ +{ + numbers: + [ + 0 + 0 + 0 + 42 + 42.1 + -5 + -5.1 + 1701 + -1701 + 12.345 + -12.345 + ] + native: + [ + true + true + false + false + null + null + ] + strings: + [ + x 0 + .0 + 00 + 01 + 0 0 0 + 42 x + 42.1 asdf + 1.2.3 + -5 0 - + -5.1 -- + 17.01e2 + + -17.01e2 : + 12345e-3 @ + -12345e-3 $ + true true + x true + false false + x false + null null + x null + ] +} \ No newline at end of file diff --git a/tests/assets/nu_json/kan_result.json b/tests/assets/nu_json/kan_result.json new file mode 100644 index 0000000000..babb9d4e06 --- /dev/null +++ b/tests/assets/nu_json/kan_result.json @@ -0,0 +1,45 @@ +{ + "numbers": [ + 0, + 0, + 0, + 42, + 42.1, + -5, + -5.1, + 1701, + -1701, + 12.345, + -12.345 + ], + "native": [ + true, + true, + false, + false, + null, + null + ], + "strings": [ + "x 0", + ".0", + "00", + "01", + "0 0 0", + "42 x", + "42.1 asdf", + "1.2.3", + "-5 0 -", + "-5.1 --", + "17.01e2 +", + "-17.01e2 :", + "12345e-3 @", + "-12345e-3 $", + "true true", + "x true", + "false false", + "x false", + "null null", + "x null" + ] +} \ No newline at end of file diff --git a/tests/assets/nu_json/kan_test.hjson b/tests/assets/nu_json/kan_test.hjson new file mode 100644 index 0000000000..1e6906a96a --- /dev/null +++ b/tests/assets/nu_json/kan_test.hjson @@ -0,0 +1,49 @@ +{ + # the comma forces a whitespace check + numbers: + [ + 0 + 0 , + -0 + 42 , + 42.1 , + -5 + -5.1 + 17.01e2 + -17.01e2 + 12345e-3 , + -12345e-3 , + ] + native: + [ + true , + true + false , + false + null , + null + ] + strings: + [ + x 0 + .0 + 00 + 01 + 0 0 0 + 42 x + 42.1 asdf + 1.2.3 + -5 0 - + -5.1 -- + 17.01e2 + + -17.01e2 : + 12345e-3 @ + -12345e-3 $ + true true + x true + false false + x false + null null + x null + ] +} diff --git a/tests/assets/nu_json/keys_result.hjson b/tests/assets/nu_json/keys_result.hjson new file mode 100644 index 0000000000..876e6c3462 --- /dev/null +++ b/tests/assets/nu_json/keys_result.hjson @@ -0,0 +1,34 @@ +{ + unquoted_key: test + _unquoted: test + test-key: test + -test: test + .key: test + trailing: test + trailing2: test + "#c1": test + "foo#bar": test + "//bar": test + "foo//bar": test + "/*foo*/": test + "foo/*foo*/bar": test + "/*": test + "foo/*bar": test + "\"": test + "foo\"bar": test + "'''": test + "foo'''bar": test + ":": test + "foo:bar": test + "{": test + "foo{bar": test + "}": test + "foo}bar": test + "[": test + "foo[bar": test + "]": test + "foo]bar": test + nl1: test + nl2: test + nl3: test +} \ No newline at end of file diff --git a/tests/assets/nu_json/keys_result.json b/tests/assets/nu_json/keys_result.json new file mode 100644 index 0000000000..81fa480b21 --- /dev/null +++ b/tests/assets/nu_json/keys_result.json @@ -0,0 +1,34 @@ +{ + "unquoted_key": "test", + "_unquoted": "test", + "test-key": "test", + "-test": "test", + ".key": "test", + "trailing": "test", + "trailing2": "test", + "#c1": "test", + "foo#bar": "test", + "//bar": "test", + "foo//bar": "test", + "/*foo*/": "test", + "foo/*foo*/bar": "test", + "/*": "test", + "foo/*bar": "test", + "\"": "test", + "foo\"bar": "test", + "'''": "test", + "foo'''bar": "test", + ":": "test", + "foo:bar": "test", + "{": "test", + "foo{bar": "test", + "}": "test", + "foo}bar": "test", + "[": "test", + "foo[bar": "test", + "]": "test", + "foo]bar": "test", + "nl1": "test", + "nl2": "test", + "nl3": "test" +} \ No newline at end of file diff --git a/tests/assets/nu_json/keys_test.hjson b/tests/assets/nu_json/keys_test.hjson new file mode 100644 index 0000000000..38f5603814 --- /dev/null +++ b/tests/assets/nu_json/keys_test.hjson @@ -0,0 +1,48 @@ +{ + # unquoted keys + unquoted_key: test + _unquoted: test + test-key: test + -test: test + .key: test + # trailing spaces in key names are ignored + trailing : test + trailing2 : test + # comment char in key name + "#c1": test + "foo#bar": test + "//bar": test + "foo//bar": test + "/*foo*/": test + "foo/*foo*/bar": test + "/*": test + "foo/*bar": test + # quotes in key name + "\"": test + "foo\"bar": test + "'''": test + "foo'''bar": test + # control char in key name + ":": test + "foo:bar": test + "{": test + "foo{bar": test + "}": test + "foo}bar": test + "[": test + "foo[bar": test + "]": test + "foo]bar": test + # newline + nl1: + test + nl2 + : + test + + nl3 + + : + + test +} diff --git a/tests/assets/nu_json/oa_result.hjson b/tests/assets/nu_json/oa_result.hjson new file mode 100644 index 0000000000..db42ac9012 --- /dev/null +++ b/tests/assets/nu_json/oa_result.hjson @@ -0,0 +1,13 @@ +[ + a + {} + {} + [] + [] + { + b: 1 + c: [] + d: {} + } + [] +] \ No newline at end of file diff --git a/tests/assets/nu_json/oa_result.json b/tests/assets/nu_json/oa_result.json new file mode 100644 index 0000000000..f0955abeb5 --- /dev/null +++ b/tests/assets/nu_json/oa_result.json @@ -0,0 +1,13 @@ +[ + "a", + {}, + {}, + [], + [], + { + "b": 1, + "c": [], + "d": {} + }, + [] +] \ No newline at end of file diff --git a/tests/assets/nu_json/oa_test.hjson b/tests/assets/nu_json/oa_test.hjson new file mode 100644 index 0000000000..35bcdb4522 --- /dev/null +++ b/tests/assets/nu_json/oa_test.hjson @@ -0,0 +1,13 @@ +[ + a + {} + {} + [] + [] + { + b: 1 + c: [] + d: {} + } + [] +] diff --git a/tests/assets/nu_json/pass1_result.hjson b/tests/assets/nu_json/pass1_result.hjson new file mode 100644 index 0000000000..2a3f4c97f1 --- /dev/null +++ b/tests/assets/nu_json/pass1_result.hjson @@ -0,0 +1,78 @@ +[ + JSON Test Pattern pass1 + { + "object with 1 member": + [ + array with 1 element + ] + } + {} + [] + -42 + true + false + null + { + integer: 1234567890 + real: -9876.54321 + e: 1.23456789e-13 + E: 1.23456789e+34 + -: 2.3456789012e+76 + zero: 0 + one: 1 + space: " " + quote: '''"''' + backslash: \ + controls: "\b\f\n\r\t" + slash: / & / + alpha: abcdefghijklmnopqrstuvwyz + ALPHA: ABCDEFGHIJKLMNOPQRSTUVWYZ + digit: 0123456789 + 0123456789: digit + special: `1~!@#$%^&*()_+-={':[,]}|;.? + hex: ģ䕧覫췯ꯍ + true: true + false: false + null: null + array: [] + object: {} + address: 50 St. James Street + url: http://www.JSON.org/ + comment: "// /* */": " " + " s p a c e d ": + [ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + ] + compact: + [ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + ] + jsontext: '''{"object with 1 member":["array with 1 element"]}''' + quotes: " " %22 0x22 034 " + "/\\\"쫾몾ꮘﳞ볚\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?": A key can be any string + } + 0.5 + 98.6 + 99.44 + 1066 + 10 + 1 + 0.1 + 1 + 2 + 2 + rosebud +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass1_result.json b/tests/assets/nu_json/pass1_result.json new file mode 100644 index 0000000000..69b354d05e --- /dev/null +++ b/tests/assets/nu_json/pass1_result.json @@ -0,0 +1,75 @@ +[ + "JSON Test Pattern pass1", + { + "object with 1 member": [ + "array with 1 element" + ] + }, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.54321, + "e": 1.23456789e-13, + "E": 1.23456789e+34, + "-": 2.3456789012e+76, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & /", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "hex": "ģ䕧覫췯ꯍ", + "true": true, + "false": false, + "null": null, + "array": [], + "object": {}, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* */": " ", + " s p a c e d ": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "compact": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7 + ], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \" %22 0x22 034 "", + "/\\\"쫾몾ꮘﳞ볚\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?": "A key can be any string" + }, + 0.5, + 98.6, + 99.44, + 1066, + 10, + 1, + 0.1, + 1, + 2, + 2, + "rosebud" +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass1_test.json b/tests/assets/nu_json/pass1_test.json new file mode 100644 index 0000000000..61cfd90c94 --- /dev/null +++ b/tests/assets/nu_json/pass1_test.json @@ -0,0 +1,58 @@ +[ + "JSON Test Pattern pass1", + {"object with 1 member":["array with 1 element"]}, + {}, + [], + -42, + true, + false, + null, + { + "integer": 1234567890, + "real": -9876.543210, + "e": 0.123456789e-12, + "E": 1.234567890E+34, + "-": 23456789012E66, + "zero": 0, + "one": 1, + "space": " ", + "quote": "\"", + "backslash": "\\", + "controls": "\b\f\n\r\t", + "slash": "/ & \/", + "alpha": "abcdefghijklmnopqrstuvwyz", + "ALPHA": "ABCDEFGHIJKLMNOPQRSTUVWYZ", + "digit": "0123456789", + "0123456789": "digit", + "special": "`1~!@#$%^&*()_+-={':[,]}|;.?", + "hex": "\u0123\u4567\u89AB\uCDEF\uabcd\uef4A", + "true": true, + "false": false, + "null": null, + "array":[ ], + "object":{ }, + "address": "50 St. James Street", + "url": "http://www.JSON.org/", + "comment": "// /* */": " ", + " s p a c e d " :[1,2 , 3 + +, + +4 , 5 , 6 ,7 ],"compact":[1,2,3,4,5,6,7], + "jsontext": "{\"object with 1 member\":[\"array with 1 element\"]}", + "quotes": "" \u0022 %22 0x22 034 "", + "\/\\\"\uCAFE\uBABE\uAB98\uFCDE\ubcda\uef4A\b\f\n\r\t`1~!@#$%^&*()_+-=[]{}|;:',./<>?" +: "A key can be any string" + }, + 0.5 ,98.6 +, +99.44 +, + +1066, +1e1, +0.1e1, +1e-1, +1e00,2e+00,2e-00 +,"rosebud"] \ No newline at end of file diff --git a/tests/assets/nu_json/pass2_result.hjson b/tests/assets/nu_json/pass2_result.hjson new file mode 100644 index 0000000000..5a9fd5e82b --- /dev/null +++ b/tests/assets/nu_json/pass2_result.hjson @@ -0,0 +1,39 @@ +[ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + Not too deep + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass2_result.json b/tests/assets/nu_json/pass2_result.json new file mode 100644 index 0000000000..2a71f5850e --- /dev/null +++ b/tests/assets/nu_json/pass2_result.json @@ -0,0 +1,39 @@ +[ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + [ + "Not too deep" + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] + ] +] \ No newline at end of file diff --git a/tests/assets/nu_json/pass2_test.json b/tests/assets/nu_json/pass2_test.json new file mode 100644 index 0000000000..d3c63c7ad8 --- /dev/null +++ b/tests/assets/nu_json/pass2_test.json @@ -0,0 +1 @@ +[[[[[[[[[[[[[[[[[[["Not too deep"]]]]]]]]]]]]]]]]]]] \ No newline at end of file diff --git a/tests/assets/nu_json/pass3_result.hjson b/tests/assets/nu_json/pass3_result.hjson new file mode 100644 index 0000000000..6db3fb61d7 --- /dev/null +++ b/tests/assets/nu_json/pass3_result.hjson @@ -0,0 +1,7 @@ +{ + "JSON Test Pattern pass3": + { + "The outermost value": must be an object or array. + "In this test": It is an object. + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/pass3_result.json b/tests/assets/nu_json/pass3_result.json new file mode 100644 index 0000000000..d98cd2f881 --- /dev/null +++ b/tests/assets/nu_json/pass3_result.json @@ -0,0 +1,6 @@ +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/pass3_test.json b/tests/assets/nu_json/pass3_test.json new file mode 100644 index 0000000000..4528d51f1a --- /dev/null +++ b/tests/assets/nu_json/pass3_test.json @@ -0,0 +1,6 @@ +{ + "JSON Test Pattern pass3": { + "The outermost value": "must be an object or array.", + "In this test": "It is an object." + } +} diff --git a/tests/assets/nu_json/pass4_result.hjson b/tests/assets/nu_json/pass4_result.hjson new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/tests/assets/nu_json/pass4_result.hjson @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/tests/assets/nu_json/pass4_result.json b/tests/assets/nu_json/pass4_result.json new file mode 100644 index 0000000000..9a037142aa --- /dev/null +++ b/tests/assets/nu_json/pass4_result.json @@ -0,0 +1 @@ +10 \ No newline at end of file diff --git a/tests/assets/nu_json/pass4_test.json b/tests/assets/nu_json/pass4_test.json new file mode 100644 index 0000000000..069c2ae6b8 --- /dev/null +++ b/tests/assets/nu_json/pass4_test.json @@ -0,0 +1,2 @@ + +10 diff --git a/tests/assets/nu_json/passSingle_result.hjson b/tests/assets/nu_json/passSingle_result.hjson new file mode 100644 index 0000000000..e580fce159 --- /dev/null +++ b/tests/assets/nu_json/passSingle_result.hjson @@ -0,0 +1 @@ +allow quoteless strings \ No newline at end of file diff --git a/tests/assets/nu_json/passSingle_result.json b/tests/assets/nu_json/passSingle_result.json new file mode 100644 index 0000000000..1829d36f86 --- /dev/null +++ b/tests/assets/nu_json/passSingle_result.json @@ -0,0 +1 @@ +"allow quoteless strings" \ No newline at end of file diff --git a/tests/assets/nu_json/passSingle_test.hjson b/tests/assets/nu_json/passSingle_test.hjson new file mode 100644 index 0000000000..e580fce159 --- /dev/null +++ b/tests/assets/nu_json/passSingle_test.hjson @@ -0,0 +1 @@ +allow quoteless strings \ No newline at end of file diff --git a/tests/assets/nu_json/root_result.hjson b/tests/assets/nu_json/root_result.hjson new file mode 100644 index 0000000000..736372f62a --- /dev/null +++ b/tests/assets/nu_json/root_result.hjson @@ -0,0 +1,7 @@ +{ + database: + { + host: 127.0.0.1 + port: 555 + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/root_result.json b/tests/assets/nu_json/root_result.json new file mode 100644 index 0000000000..21b01cd003 --- /dev/null +++ b/tests/assets/nu_json/root_result.json @@ -0,0 +1,6 @@ +{ + "database": { + "host": "127.0.0.1", + "port": 555 + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/root_test.hjson b/tests/assets/nu_json/root_test.hjson new file mode 100644 index 0000000000..c0acd16eeb --- /dev/null +++ b/tests/assets/nu_json/root_test.hjson @@ -0,0 +1,6 @@ +// a object with the root braces omitted +database: +{ + host: 127.0.0.1 + port: 555 +} diff --git a/tests/assets/nu_json/stringify1_result.hjson b/tests/assets/nu_json/stringify1_result.hjson new file mode 100644 index 0000000000..77b2eddc13 --- /dev/null +++ b/tests/assets/nu_json/stringify1_result.hjson @@ -0,0 +1,49 @@ +{ + quotes: + { + num1: "1,2" + num2: "-1.1 ," + num3: "1e10 ,2" + num4: "-1e-10," + kw1: "true," + kw2: "false ," + kw3: "null,123" + close1: "1}" + close1b: "1 }" + close2: "1]" + close2b: "1 ]" + close3: "1," + close3b: "1 ," + comment1: "1#str" + comment2: "1//str" + comment3: "1/*str*/" + punc1: "{" + punc1b: "{foo" + punc2: "}" + punc2b: "}foo" + punc3: "[" + punc3b: "[foo" + punc4: "]" + punc4b: "]foo" + punc5: "," + punc5b: ",foo" + punc6: ":" + punc6b: ":foo" + } + noquotes: + { + num0: .1,2 + num1: 1.1.1,2 + num2: -.1, + num3: 1e10e,2 + num4: -1e--10, + kw1: true1, + kw2: false0, + kw3: null0, + close1: a} + close2: a] + comment1: a#str + comment2: a//str + comment3: a/*str*/ + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/stringify1_result.json b/tests/assets/nu_json/stringify1_result.json new file mode 100644 index 0000000000..12514f8786 --- /dev/null +++ b/tests/assets/nu_json/stringify1_result.json @@ -0,0 +1,47 @@ +{ + "quotes": { + "num1": "1,2", + "num2": "-1.1 ,", + "num3": "1e10 ,2", + "num4": "-1e-10,", + "kw1": "true,", + "kw2": "false ,", + "kw3": "null,123", + "close1": "1}", + "close1b": "1 }", + "close2": "1]", + "close2b": "1 ]", + "close3": "1,", + "close3b": "1 ,", + "comment1": "1#str", + "comment2": "1//str", + "comment3": "1/*str*/", + "punc1": "{", + "punc1b": "{foo", + "punc2": "}", + "punc2b": "}foo", + "punc3": "[", + "punc3b": "[foo", + "punc4": "]", + "punc4b": "]foo", + "punc5": ",", + "punc5b": ",foo", + "punc6": ":", + "punc6b": ":foo" + }, + "noquotes": { + "num0": ".1,2", + "num1": "1.1.1,2", + "num2": "-.1,", + "num3": "1e10e,2", + "num4": "-1e--10,", + "kw1": "true1,", + "kw2": "false0,", + "kw3": "null0,", + "close1": "a}", + "close2": "a]", + "comment1": "a#str", + "comment2": "a//str", + "comment3": "a/*str*/" + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/stringify1_test.hjson b/tests/assets/nu_json/stringify1_test.hjson new file mode 100644 index 0000000000..b353bf19cf --- /dev/null +++ b/tests/assets/nu_json/stringify1_test.hjson @@ -0,0 +1,50 @@ +// test if stringify produces correct output +{ + quotes: + { + num1: "1,2" + num2: "-1.1 ," + num3: "1e10 ,2" + num4: "-1e-10," + kw1: "true," + kw2: "false ," + kw3: "null,123" + close1: "1}" + close1b: "1 }" + close2: "1]" + close2b: "1 ]" + close3: "1," + close3b: "1 ," + comment1: "1#str" + comment2: "1//str" + comment3: "1/*str*/" + punc1: "{" + punc1b: "{foo" + punc2: "}" + punc2b: "}foo" + punc3: "[" + punc3b: "[foo" + punc4: "]" + punc4b: "]foo" + punc5: "," + punc5b: ",foo" + punc6: ":" + punc6b: ":foo" + } + noquotes: + { + num0: ".1,2" + num1: "1.1.1,2" + num2: "-.1," + num3: "1e10e,2" + num4: "-1e--10," + kw1: "true1," + kw2: "false0," + kw3: "null0," + close1: "a}" + close2: "a]" + comment1: "a#str" + comment2: "a//str" + comment3: "a/*str*/" + } +} diff --git a/tests/assets/nu_json/strings_result.hjson b/tests/assets/nu_json/strings_result.hjson new file mode 100644 index 0000000000..6ef06f8741 --- /dev/null +++ b/tests/assets/nu_json/strings_result.hjson @@ -0,0 +1,75 @@ +{ + text1: This is a valid string value. + text2: a \ is just a \ + text3: "You need quotes\tfor escapes" + text4a: " untrimmed " + text4b: " untrimmed" + text4c: "untrimmed " + multiline1: + ''' + first line + indented line + last line + ''' + multiline2: + ''' + first line + indented line + last line + ''' + multiline3: + ''' + first line + indented line + last line + + ''' + foo1a: asdf\"'a\s\w + foo1b: asdf\"'a\s\w + foo1c: asdf\"'a\s\w + foo2a: '''"asdf"''' + foo2b: '''"asdf"''' + foo3a: asdf''' + foo3b: "'''asdf" + foo4a: "asdf'''\nasdf" + foo4b: "asdf\n'''asdf" + arr: + [ + one + two + three + four + ] + not: + { + number: 5 + negative: -4.2 + yes: true + no: false + null: null + array: + [ + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 0 + -1 + 0.5 + ] + } + special: + { + true: "true" + false: "false" + null: "null" + one: "1" + two: "2" + minus: "-3" + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/strings_result.json b/tests/assets/nu_json/strings_result.json new file mode 100644 index 0000000000..16321ba7a9 --- /dev/null +++ b/tests/assets/nu_json/strings_result.json @@ -0,0 +1,55 @@ +{ + "text1": "This is a valid string value.", + "text2": "a \\ is just a \\", + "text3": "You need quotes\tfor escapes", + "text4a": " untrimmed ", + "text4b": " untrimmed", + "text4c": "untrimmed ", + "multiline1": "first line\n indented line\nlast line", + "multiline2": "first line\n indented line\nlast line", + "multiline3": "first line\n indented line\nlast line\n", + "foo1a": "asdf\\\"'a\\s\\w", + "foo1b": "asdf\\\"'a\\s\\w", + "foo1c": "asdf\\\"'a\\s\\w", + "foo2a": "\"asdf\"", + "foo2b": "\"asdf\"", + "foo3a": "asdf'''", + "foo3b": "'''asdf", + "foo4a": "asdf'''\nasdf", + "foo4b": "asdf\n'''asdf", + "arr": [ + "one", + "two", + "three", + "four" + ], + "not": { + "number": 5, + "negative": -4.2, + "yes": true, + "no": false, + "null": null, + "array": [ + 1, + 2, + 3, + 4, + 5, + 6, + 7, + 8, + 9, + 0, + -1, + 0.5 + ] + }, + "special": { + "true": "true", + "false": "false", + "null": "null", + "one": "1", + "two": "2", + "minus": "-3" + } +} \ No newline at end of file diff --git a/tests/assets/nu_json/strings_test.hjson b/tests/assets/nu_json/strings_test.hjson new file mode 100644 index 0000000000..616895209f --- /dev/null +++ b/tests/assets/nu_json/strings_test.hjson @@ -0,0 +1,80 @@ +{ + # simple + + text1: This is a valid string value. + text2:a \ is just a \ + + text3: "You need quotes\tfor escapes" + + text4a: " untrimmed " + text4b: " untrimmed" + text4c: "untrimmed " + + # multiline string + + multiline1: + ''' + first line + indented line + last line + ''' + + multiline2: + '''first line + indented line + last line''' + + multiline3: + ''' + first line + indented line + last line + + ''' # trailing lf + + # escapes/no escape + + foo1a: asdf\"'a\s\w + foo1b: '''asdf\"'a\s\w''' + foo1c: "asdf\\\"'a\\s\\w" + + foo2a: "\"asdf\"" + foo2b: '''"asdf"''' + + foo3a: "asdf'''" + foo3b: "'''asdf" + + foo4a: "asdf'''\nasdf" + foo4b: "asdf\n'''asdf" + + # in arrays + arr: + [ + one + two + "three" + '''four''' + ] + + # not strings + not: + { + number: 5 + negative: -4.2 + yes: true + no: false + null: null + array: [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, -1, 0.5 ] + } + + # special quoted + special: + { + true: "true" + false: "false" + null: "null" + one: "1" + two: "2" + minus: "-3" + } +} diff --git a/tests/assets/nu_json/testlist.txt b/tests/assets/nu_json/testlist.txt new file mode 100644 index 0000000000..debb52c83e --- /dev/null +++ b/tests/assets/nu_json/testlist.txt @@ -0,0 +1,75 @@ +charset_test.hjson +comments_test.hjson +empty_test.hjson +failCharset1_test.hjson +failJSON02_test.json +failJSON05_test.json +failJSON06_test.json +failJSON07_test.json +failJSON08_test.json +failJSON10_test.json +failJSON11_test.json +failJSON12_test.json +failJSON13_test.json +failJSON14_test.json +failJSON15_test.json +failJSON16_test.json +failJSON17_test.json +failJSON19_test.json +failJSON20_test.json +failJSON21_test.json +failJSON22_test.json +failJSON23_test.json +failJSON24_test.json +failJSON26_test.json +failJSON28_test.json +failJSON29_test.json +failJSON30_test.json +failJSON31_test.json +failJSON32_test.json +failJSON33_test.json +failJSON34_test.json +failKey1_test.hjson +failKey2_test.hjson +failKey3_test.hjson +failKey4_test.hjson +failMLStr1_test.hjson +failObj1_test.hjson +failObj2_test.hjson +failObj3_test.hjson +failStr1a_test.hjson +failStr1b_test.hjson +failStr1c_test.hjson +failStr1d_test.hjson +failStr2a_test.hjson +failStr2b_test.hjson +failStr2c_test.hjson +failStr2d_test.hjson +failStr3a_test.hjson +failStr3b_test.hjson +failStr3c_test.hjson +failStr3d_test.hjson +failStr4a_test.hjson +failStr4b_test.hjson +failStr4c_test.hjson +failStr4d_test.hjson +failStr5a_test.hjson +failStr5b_test.hjson +failStr5c_test.hjson +failStr5d_test.hjson +failStr6a_test.hjson +failStr6b_test.hjson +failStr6c_test.hjson +failStr6d_test.hjson +kan_test.hjson +keys_test.hjson +oa_test.hjson +pass1_test.json +pass2_test.json +pass3_test.json +pass4_test.json +passSingle_test.hjson +root_test.hjson +stringify1_test.hjson +strings_test.hjson +trail_test.hjson \ No newline at end of file diff --git a/tests/assets/nu_json/trail_result.hjson b/tests/assets/nu_json/trail_result.hjson new file mode 100644 index 0000000000..57ffc716bd --- /dev/null +++ b/tests/assets/nu_json/trail_result.hjson @@ -0,0 +1,3 @@ +{ + foo: 0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1 +} \ No newline at end of file diff --git a/tests/assets/nu_json/trail_result.json b/tests/assets/nu_json/trail_result.json new file mode 100644 index 0000000000..451c8ceb94 --- /dev/null +++ b/tests/assets/nu_json/trail_result.json @@ -0,0 +1,3 @@ +{ + "foo": "0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1" +} \ No newline at end of file diff --git a/tests/assets/nu_json/trail_test.hjson b/tests/assets/nu_json/trail_test.hjson new file mode 100644 index 0000000000..62d98e98a3 --- /dev/null +++ b/tests/assets/nu_json/trail_test.hjson @@ -0,0 +1,2 @@ +// the following line contains trailing whitespace: +foo: 0 -- this string starts at 0 and ends at 1, preceding and trailing whitespace is ignored -- 1 diff --git a/tests/fixtures/formats/appveyor.yml b/tests/fixtures/formats/appveyor.yml new file mode 100644 index 0000000000..770f32a2a9 --- /dev/null +++ b/tests/fixtures/formats/appveyor.yml @@ -0,0 +1,31 @@ +image: Visual Studio 2017 + +environment: + global: + PROJECT_NAME: nushell + RUST_BACKTRACE: 1 + matrix: + - TARGET: x86_64-pc-windows-msvc + CHANNEL: nightly + BITS: 64 + +install: + - set PATH=C:\msys64\mingw%BITS%\bin;C:\msys64\usr\bin;%PATH% + - curl -sSf -o rustup-init.exe https://win.rustup.rs + # Install rust + - rustup-init.exe -y --default-host %TARGET% --default-toolchain %CHANNEL%-%TARGET% + - set PATH=%PATH%;C:\Users\appveyor\.cargo\bin + # Required for Racer autoconfiguration + - call "C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Auxiliary\Build\vcvars64.bat" + + +build: false + +test_script: + # compile #[cfg(not(test))] code + - cargo build --verbose + - cargo test --all --verbose + +cache: + - target -> Cargo.lock + - C:\Users\appveyor\.cargo\registry -> Cargo.lock diff --git a/tests/fixtures/formats/caco3_plastics.csv b/tests/fixtures/formats/caco3_plastics.csv new file mode 100644 index 0000000000..8b04b00c0e --- /dev/null +++ b/tests/fixtures/formats/caco3_plastics.csv @@ -0,0 +1,10 @@ +importer,shipper,tariff_item,name,origin,shipped_at,arrived_at,net_weight,fob_price,cif_price,cif_per_net_weight +PLASTICOS RIVAL CIA LTDA,S A REVERTE,2509000000,CARBONATO DE CALCIO TIPO CALCIPORE 160 T AL,SPAIN,18/03/2016,17/04/2016,"81,000.00","14,417.58","18,252.34",0.23 +MEXICHEM ECUADOR S.A.,OMYA ANDINA S A,2836500000,CARBONATO,COLOMBIA,07/07/2016,10/07/2016,"26,000.00","7,072.00","8,127.18",0.31 +PLASTIAZUAY SA,SA REVERTE,2836500000,CARBONATO DE CALCIO,SPAIN,27/07/2016,09/08/2016,"81,000.00","8,100.00","11,474.55",0.14 +PLASTICOS RIVAL CIA LTDA,AND ENDUSTRIYEL HAMMADDELER DIS TCARET LTD.STI.,2836500000,CALCIUM CARBONATE ANADOLU ANDCARB CT-1,TURKEY,04/10/2016,11/11/2016,"100,000.00","17,500.00","22,533.75",0.23 +QUIMICA COMERCIAL QUIMICIAL CIA. LTDA.,SA REVERTE,2836500000,CARBONATO DE CALCIO,SPAIN,24/06/2016,12/07/2016,"27,000.00","3,258.90","5,585.00",0.21 +PICA PLASTICOS INDUSTRIALES C.A.,OMYA ANDINA S.A,3824909999,CARBONATO DE CALCIO,COLOMBIA,01/01/1900,18/01/2016,"66,500.00","12,635.00","18,670.52",0.28 +PLASTIQUIM S.A.,OMYA ANDINA S.A NIT 830.027.386-6,3824909999,CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYA CARB 1T CG BBS 1000,COLOMBIA,01/01/1900,25/10/2016,"33,000.00","6,270.00","9,999.00",0.30 +QUIMICOS ANDINOS QUIMANDI S.A.,SIBELCO COLOMBIA SAS,3824909999,CARBONATO DE CALCIO RECUBIERTO,COLOMBIA,01/11/2016,03/11/2016,"52,000.00","8,944.00","13,039.05",0.25 +TIGRE ECUADOR S.A. ECUATIGRE,OMYA ANDINA S.A NIT 830.027.386-6,3824909999,CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYACARB 1T CG BPA 25 NO,COLOMBIA,01/01/1900,28/10/2016,"66,000.00","11,748.00","18,216.00",0.28 diff --git a/tests/fixtures/formats/caco3_plastics.tsv b/tests/fixtures/formats/caco3_plastics.tsv new file mode 100644 index 0000000000..071baaae30 --- /dev/null +++ b/tests/fixtures/formats/caco3_plastics.tsv @@ -0,0 +1,10 @@ +importer shipper tariff_item name origin shipped_at arrived_at net_weight fob_price cif_price cif_per_net_weight +PLASTICOS RIVAL CIA LTDA S A REVERTE 2509000000 CARBONATO DE CALCIO TIPO CALCIPORE 160 T AL SPAIN 18/03/2016 17/04/2016 81,000.00 14,417.58 18,252.34 0.23 +MEXICHEM ECUADOR S.A. OMYA ANDINA S A 2836500000 CARBONATO COLOMBIA 07/07/2016 10/07/2016 26,000.00 7,072.00 8,127.18 0.31 +PLASTIAZUAY SA SA REVERTE 2836500000 CARBONATO DE CALCIO SPAIN 27/07/2016 09/08/2016 81,000.00 8,100.00 11,474.55 0.14 +PLASTICOS RIVAL CIA LTDA AND ENDUSTRIYEL HAMMADDELER DIS TCARET LTD.STI. 2836500000 CALCIUM CARBONATE ANADOLU ANDCARB CT-1 TURKEY 04/10/2016 11/11/2016 100,000.00 17,500.00 22,533.75 0.23 +QUIMICA COMERCIAL QUIMICIAL CIA. LTDA. SA REVERTE 2836500000 CARBONATO DE CALCIO SPAIN 24/06/2016 12/07/2016 27,000.00 3,258.90 5,585.00 0.21 +PICA PLASTICOS INDUSTRIALES C.A. OMYA ANDINA S.A 3824909999 CARBONATO DE CALCIO COLOMBIA 01/01/1900 18/01/2016 66,500.00 12,635.00 18,670.52 0.28 +PLASTIQUIM S.A. OMYA ANDINA S.A NIT 830.027.386-6 3824909999 CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYA CARB 1T CG BBS 1000 COLOMBIA 01/01/1900 25/10/2016 33,000.00 6,270.00 9,999.00 0.30 +QUIMICOS ANDINOS QUIMANDI S.A. SIBELCO COLOMBIA SAS 3824909999 CARBONATO DE CALCIO RECUBIERTO COLOMBIA 01/11/2016 03/11/2016 52,000.00 8,944.00 13,039.05 0.25 +TIGRE ECUADOR S.A. ECUATIGRE OMYA ANDINA S.A NIT 830.027.386-6 3824909999 CARBONATO DE CALCIO RECUBIERTO CON ACIDO ESTEARICO OMYACARB 1T CG BPA 25 NO COLOMBIA 01/01/1900 28/10/2016 66,000.00 11,748.00 18,216.00 0.28 diff --git a/tests/fixtures/formats/cargo_sample.toml b/tests/fixtures/formats/cargo_sample.toml new file mode 100644 index 0000000000..c36a40e0ad --- /dev/null +++ b/tests/fixtures/formats/cargo_sample.toml @@ -0,0 +1,55 @@ +[package] +name = "nu" +version = "0.1.1" +authors = ["The Nu Project Contributors"] +description = "a new type of shell" +license = "ISC" +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +rustyline = "4.1.0" +sysinfo = "0.8.4" +chrono = { version = "0.4.6", features = ["serde"] } +chrono-tz = "0.5.1" +derive-new = "0.5.6" +prettytable-rs = "0.8.0" +itertools = "0.8.0" +ansi_term = "0.11.0" +conch-parser = "0.1.1" +nom = "5.0.0-beta1" +dunce = "1.0.0" +indexmap = { version = "1.0.2", features = ["serde-1"] } +chrono-humanize = "0.0.11" +byte-unit = "2.1.0" +ordered-float = "1.0.2" +prettyprint = "0.6.0" +cursive = { version = "0.12.0", features = ["pancurses-backend"], default-features = false } +futures-preview = { version = "0.3.0-alpha.16", features = ["compat", "io-compat"] } +futures-sink-preview = "0.3.0-alpha.16" +tokio-fs = "0.1.6" +futures_codec = "0.2.2" +term = "0.5.2" +bytes = "0.4.12" +log = "0.4.6" +pretty_env_logger = "0.3.0" +lalrpop-util = "0.17.0" +regex = "1.1.6" +serde = "1.0.91" +serde_json = "1.0.39" +serde_derive = "1.0.91" +getset = "0.0.7" +logos = "0.10.0-rc2" +logos-derive = "0.10.0-rc2" +language-reporting = "0.3.0" +directories = "2.0.2" +toml = "0.5.1" +toml-query = "0.9.0" + +[dependencies.pancurses] +version = "0.16" +features = ["win32a"] + +[dev-dependencies] +pretty_assertions = "0.6.1" diff --git a/tests/fixtures/formats/jonathan.xml b/tests/fixtures/formats/jonathan.xml new file mode 100644 index 0000000000..0ce0016c19 --- /dev/null +++ b/tests/fixtures/formats/jonathan.xml @@ -0,0 +1,22 @@ + + + + Jonathan Turner + http://www.jonathanturner.org + + + + Creating crossplatform Rust terminal apps + <p><img src="/images/pikachu.jpg" alt="Pikachu animation in Windows" /></p> + +<p><em>Look Mom, Pikachu running in Windows CMD!</em></p> + +<p>Part of the adventure is not seeing the way ahead and going anyway.</p> + + Mon, 05 Oct 2015 00:00:00 +0000 + http://www.jonathanturner.org/2015/10/off-to-new-adventures.html + http://www.jonathanturner.org/2015/10/off-to-new-adventures.html + + + + diff --git a/tests/fixtures/formats/lines_test.txt b/tests/fixtures/formats/lines_test.txt new file mode 100644 index 0000000000..034e56b61e --- /dev/null +++ b/tests/fixtures/formats/lines_test.txt @@ -0,0 +1,2 @@ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx +yyy diff --git a/tests/fixtures/formats/random_numbers.csv b/tests/fixtures/formats/random_numbers.csv new file mode 100644 index 0000000000..5e5c694657 --- /dev/null +++ b/tests/fixtures/formats/random_numbers.csv @@ -0,0 +1,51 @@ +random numbers +5 +2 +0 +5 +1 +3 +5 +2 +1 +1 +0 +5 +1 +0 +0 +4 +0 +4 +5 +2 +2 +4 +3 +2 +5 +3 +1 +0 +5 +1 +2 +2 +5 +0 +1 +1 +5 +1 +1 +3 +3 +1 +5 +0 +2 +1 +3 +1 +1 +2 diff --git a/tests/fixtures/formats/sample-ls-output.json b/tests/fixtures/formats/sample-ls-output.json new file mode 100644 index 0000000000..4636f0353d --- /dev/null +++ b/tests/fixtures/formats/sample-ls-output.json @@ -0,0 +1 @@ +[{"name":"a.txt","type":"File","size":3444,"modified":"2020-07-1918:26:30.560716967UTC"},{"name":"B.txt","type":"File","size":1341,"modified":"2020-07-1918:26:30.561021953UTC"},{"name":"C","type":"Dir","size":118253,"modified":"2020-07-1918:26:30.562092480UTC"}] diff --git a/tests/fixtures/formats/sample-ps-output.json b/tests/fixtures/formats/sample-ps-output.json new file mode 100644 index 0000000000..7ae34d66e9 --- /dev/null +++ b/tests/fixtures/formats/sample-ps-output.json @@ -0,0 +1 @@ +[{"pid":10390,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":132112384,"virtual":4989624320},{"pid":10461,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":126992384,"virtual":4995346432},{"pid":10530,"name":"kworker/6:1-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":10593,"name":"kworker/1:1-mm_percpu_wq","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":10650,"name":"chrome","status":"Sleeping","cpu":8.026974,"mem":262434816,"virtual":5217419264},{"pid":10803,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":48173056,"virtual":542531584},{"pid":11191,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":124092416,"virtual":4975763456},{"pid":11210,"name":"kworker/7:0-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":11254,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":113070080,"virtual":4971659264},{"pid":11279,"name":"kworker/u16:0-events_unbound","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":11476,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":88993792,"virtual":4937097216},{"pid":12755,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":163397632,"virtual":5034328064},{"pid":12772,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":113561600,"virtual":4985073664},{"pid":14351,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":111861760,"virtual":4962754560},{"pid":17818,"name":"udisksd","status":"Sleeping","cpu":0.0,"mem":14409728,"virtual":402935808},{"pid":17815,"name":".gvfs-udisks2-v","status":"Sleeping","cpu":0.0,"mem":16199680,"virtual":585306112},{"pid":17831,"name":".gvfs-mtp-volum","status":"Sleeping","cpu":0.0,"mem":6393856,"virtual":454680576},{"pid":17836,"name":".gvfs-gphoto2-v","status":"Sleeping","cpu":0.0,"mem":7110656,"virtual":456966144},{"pid":17841,"name":".gvfs-afc-volum","status":"Sleeping","cpu":0.0,"mem":8585216,"virtual":537448448},{"pid":17846,"name":".gvfsd-trash-wr","status":"Sleeping","cpu":0.0,"mem":12767232,"virtual":577998848},{"pid":17856,"name":".gvfsd-network-","status":"Sleeping","cpu":0.0,"mem":13295616,"virtual":654110720},{"pid":17862,"name":".gvfsd-dnssd-wr","status":"Sleeping","cpu":0.0,"mem":7639040,"virtual":533233664},{"pid":17869,"name":"dconf-service","status":"Sleeping","cpu":0.0,"mem":5365760,"virtual":158957568},{"pid":18153,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":183738368,"virtual":5128962048},{"pid":23033,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":166035456,"virtual":5074878464},{"pid":24101,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":101224448,"virtual":4956262400},{"pid":24832,"name":"kworker/7:2-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":24912,"name":"kworker/5:2-events_power_efficient","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":25228,"name":"kworker/4:3-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":25678,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":117522432,"virtual":4970983424},{"pid":25706,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":30760960,"virtual":528375808},{"pid":26080,"name":"kworker/1:0-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":26818,"name":"kworker/2:0-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":26827,"name":"kworker/6:2-mm_percpu_wq","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":26832,"name":"kworker/0:2-mm_percpu_wq","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":26843,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":116621312,"virtual":4982403072},{"pid":27163,"name":"kworker/3:2-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":27800,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":128200704,"virtual":4965363712},{"pid":27820,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":54960128,"virtual":4895596544},{"pid":27898,"name":"kworker/3:0-mm_percpu_wq","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":27977,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":141930496,"virtual":4982546432},{"pid":28035,"name":"kworker/u16:1-events_unbound","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":28104,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":126853120,"virtual":5003902976},{"pid":28158,"name":"nu","status":"Sleeping","cpu":0.0,"mem":27344896,"virtual":870764544},{"pid":28236,"name":"chrome","status":"Sleeping","cpu":0.0,"mem":450560000,"virtual":5389582336},{"pid":29186,"name":"kworker/5:0-events","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":30140,"name":"kworker/u16:2-events_unbound","status":"Idle","cpu":0.0,"mem":0,"virtual":0},{"pid":30142,"name":"nu_plugin_core_","status":"Zombie","cpu":0.0,"mem":0,"virtual":0},{"pid":30356,"name":"sh","status":"Sleeping","cpu":0.0,"mem":3743744,"virtual":224092160},{"pid":30360,"name":"nu_plugin_core_ps","status":"Sleeping","cpu":80.23046000000001,"mem":6422528,"virtual":633016320}] \ No newline at end of file diff --git a/tests/fixtures/formats/sample-simple.json b/tests/fixtures/formats/sample-simple.json new file mode 100644 index 0000000000..7986b6e546 --- /dev/null +++ b/tests/fixtures/formats/sample-simple.json @@ -0,0 +1,4 @@ +{ + "first": "first", + "second": "this\nshould\nbe\nseparate\nlines" +} \ No newline at end of file diff --git a/tests/fixtures/formats/sample-sys-output.json b/tests/fixtures/formats/sample-sys-output.json new file mode 100644 index 0000000000..122fd85941 --- /dev/null +++ b/tests/fixtures/formats/sample-sys-output.json @@ -0,0 +1,125 @@ +{ + "host": { + "name": "Linux", + "release": "5.4.33", + "version": "#1-NixOS SMP Fri Apr 17 08:50:26 UTC 2020", + "hostname": "nixos", + "arch": "x86_64", + "uptime": 105126, + "sessions": [ + "alexj" + ] + }, + "cpu": { + "cores": 8, + "current ghz": 2.4200000000000004, + "min ghz": 0.39999999999999997, + "max ghz": 3.4000000000000004 + }, + "disks": [ + { + "device": "/dev/disk/by-uuid/e9adff48-c37b-4631-b98b-eaec9b410ba3", + "type": "ext4", + "mount": "/", + "total": 483445473280, + "used": 121866776576, + "free": 336949624832 + }, + { + "device": "/dev/disk/by-uuid/e9adff48-c37b-4631-b98b-eaec9b410ba3", + "type": "ext4", + "mount": "/nix/store", + "total": 483445473280, + "used": 121866776576, + "free": 336949624832 + }, + { + "device": "/dev/sda3", + "type": "vfat", + "mount": "/boot", + "total": 534757376, + "used": 72650752, + "free": 462106624 + } + ], + "mem": { + "total": 16256524000, + "free": 3082268000, + "swap total": 18874344000, + "swap free": 18874344000 + }, + "temp": [ + { + "unit": "iwlwifi_1", + "temp": 42.0 + }, + { + "unit": "acpitz", + "temp": 53.00000000000001, + "critical": 103.0 + }, + { + "unit": "coretemp", + "label": "Core 1", + "temp": 52.00000000000001, + "high": 100.0, + "critical": 100.0 + }, + { + "unit": "coretemp", + "label": "Core 2", + "temp": 52.00000000000001, + "high": 100.0, + "critical": 100.0 + }, + { + "unit": "coretemp", + "label": "Package id 0", + "temp": 52.00000000000001, + "high": 100.0, + "critical": 100.0 + }, + { + "unit": "coretemp", + "label": "Core 3", + "temp": 51.00000000000001, + "high": 100.0, + "critical": 100.0 + }, + { + "unit": "coretemp", + "label": "Core 0", + "temp": 51.00000000000001, + "high": 100.0, + "critical": 100.0 + }, + { + "unit": "pch_skylake", + "temp": 48.00000000000001 + } + ], + "net": [ + { + "name": "wlp2s0", + "sent": 387642399, + "recv": 15324719784 + }, + { + "name": "lo", + "sent": 2667, + "recv": 2667 + }, + { + "name": "vboxnet0", + "sent": 0, + "recv": 0 + } + ], + "battery": [ + { + "vendor": "ASUSTeK", + "model": "ASUS Battery", + "cycles": 445 + } + ] +} diff --git a/tests/fixtures/formats/sample.bson b/tests/fixtures/formats/sample.bson new file mode 100644 index 0000000000..ff51ae2072 Binary files /dev/null and b/tests/fixtures/formats/sample.bson differ diff --git a/tests/fixtures/formats/sample.db b/tests/fixtures/formats/sample.db new file mode 100644 index 0000000000..df67bfce3b Binary files /dev/null and b/tests/fixtures/formats/sample.db differ diff --git a/tests/fixtures/formats/sample.eml b/tests/fixtures/formats/sample.eml new file mode 100644 index 0000000000..d17e28868f --- /dev/null +++ b/tests/fixtures/formats/sample.eml @@ -0,0 +1,20 @@ +MIME-Version: 1.0 +Date: Fri, 28 Aug 2020 13:59:02 -0700 +Message-ID: +Subject: Test Message +From: "from@example.com" +Reply-To: "replyto@example.com" +To: to@example.com +Content-Type: multipart/alternative; boundary="0000000000009d71fb05adf6528a" + +--0000000000009d71fb05adf6528a +Content-Type: text/plain; charset="UTF-8" + +Test Message + +--0000000000009d71fb05adf6528a +Content-Type: text/html; charset="UTF-8" + +
Test Message
+ +--0000000000009d71fb05adf6528a-- diff --git a/tests/fixtures/formats/sample.ini b/tests/fixtures/formats/sample.ini new file mode 100644 index 0000000000..c8f2485287 --- /dev/null +++ b/tests/fixtures/formats/sample.ini @@ -0,0 +1,19 @@ +[SectionOne] + +key = value +integer = 1234 +real = 3.14 +string1 = 'Case 1' +string2 = "Case 2" + +[SectionTwo] + +; comment line +key = new value +integer = 5678 +real = 3.14 +string1 = 'Case 1' +string2 = "Case 2" +string3 = 'Case 3' + + diff --git a/tests/fixtures/formats/sample.url b/tests/fixtures/formats/sample.url new file mode 100644 index 0000000000..361d70dbb6 --- /dev/null +++ b/tests/fixtures/formats/sample.url @@ -0,0 +1 @@ +bread=baguette&cheese=comt%C3%A9&meat=ham&fat=butter \ No newline at end of file diff --git a/tests/fixtures/formats/sample_data.ods b/tests/fixtures/formats/sample_data.ods new file mode 100644 index 0000000000..5bcd2bda44 Binary files /dev/null and b/tests/fixtures/formats/sample_data.ods differ diff --git a/tests/fixtures/formats/sample_data.xlsx b/tests/fixtures/formats/sample_data.xlsx new file mode 100644 index 0000000000..1cd1b832a4 Binary files /dev/null and b/tests/fixtures/formats/sample_data.xlsx differ diff --git a/tests/fixtures/formats/sample_headers.xlsx b/tests/fixtures/formats/sample_headers.xlsx new file mode 100644 index 0000000000..f05428dd58 Binary files /dev/null and b/tests/fixtures/formats/sample_headers.xlsx differ diff --git a/tests/fixtures/formats/script.nu b/tests/fixtures/formats/script.nu new file mode 100755 index 0000000000..b79dfdcbbc --- /dev/null +++ b/tests/fixtures/formats/script.nu @@ -0,0 +1,2 @@ +#!/Users/gedge/src/github.com/nushell/nushell/target/debug/nu +echo "done" diff --git a/tests/fixtures/formats/script_multiline.nu b/tests/fixtures/formats/script_multiline.nu new file mode 100644 index 0000000000..389a3148a5 --- /dev/null +++ b/tests/fixtures/formats/script_multiline.nu @@ -0,0 +1,2 @@ +echo [1 4] | length +echo [2 3 5] | length diff --git a/tests/fixtures/formats/sgml_description.json b/tests/fixtures/formats/sgml_description.json new file mode 100644 index 0000000000..d76af0fe9c --- /dev/null +++ b/tests/fixtures/formats/sgml_description.json @@ -0,0 +1,30 @@ +{ + "glossary": { + "title": "example glossary", + "GlossDiv": { + "title": "S", + "GlossList": { + "GlossEntry": { + "ID": "SGML", + "SortAs": "SGML", + "GlossTerm": "Standard Generalized Markup Language", + "Acronym": "SGML", + "Abbrev": "ISO 8879:1986", + "Height": 10, + "GlossDef": { + "para": "A meta-markup language, used to create markup languages such as DocBook.", + "GlossSeeAlso": [ + "GML", + "XML" + ] + }, + "Sections": [ + 101, + 102 + ], + "GlossSee": "markup" + } + } + } + } +} \ No newline at end of file diff --git a/tests/fixtures/formats/utf16.ini b/tests/fixtures/formats/utf16.ini new file mode 100644 index 0000000000..c68b44ccdb Binary files /dev/null and b/tests/fixtures/formats/utf16.ini differ diff --git a/tests/fixtures/playground/config/default.toml b/tests/fixtures/playground/config/default.toml new file mode 100644 index 0000000000..5939aacd41 --- /dev/null +++ b/tests/fixtures/playground/config/default.toml @@ -0,0 +1,3 @@ +skip_welcome_message = true +filesize_format = "auto" +rm_always_trash = false diff --git a/tests/fixtures/playground/config/startup.toml b/tests/fixtures/playground/config/startup.toml new file mode 100644 index 0000000000..c4b9e06785 --- /dev/null +++ b/tests/fixtures/playground/config/startup.toml @@ -0,0 +1,3 @@ +skip_welcome_message = true + +startup = ["def hello-world [] { echo 'Nu World' }"] diff --git a/tests/main.rs b/tests/main.rs new file mode 100644 index 0000000000..30f5e27fe2 --- /dev/null +++ b/tests/main.rs @@ -0,0 +1,5 @@ +extern crate nu_test_support; + +mod path; +mod plugins; +mod shell; diff --git a/tests/path/canonicalize.rs b/tests/path/canonicalize.rs new file mode 100644 index 0000000000..937af3f49e --- /dev/null +++ b/tests/path/canonicalize.rs @@ -0,0 +1,423 @@ +use std::path::Path; + +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; + +use nu_path::canonicalize_with; + +#[test] +fn canonicalize_path() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let mut spam = dirs.test().clone(); + spam.push("spam.txt"); + + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = canonicalize_with(spam, cwd).expect("Failed to canonicalize"); + + assert!(actual.ends_with("spam.txt")); + }); +} + +#[test] +fn canonicalize_unicode_path() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("🚒.txt")]); + + let mut spam = dirs.test().clone(); + spam.push("🚒.txt"); + + let cwd = std::env::current_dir().expect("Could not get current directory"); + + let actual = canonicalize_with(spam, cwd).expect("Failed to canonicalize"); + + assert!(actual.ends_with("🚒.txt")); + }); +} + +#[ignore] +#[test] +fn canonicalize_non_utf8_path() { + // TODO +} + +#[test] +fn canonicalize_path_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with("spam.txt", dirs.test()).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_unicode_path_relative_to_unicode_path_with_spaces() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("e-$ èрт🚒♞中片-j"); + sandbox.with_files(vec![EmptyFile("e-$ èрт🚒♞中片-j/🚒.txt")]); + + let mut relative_to = dirs.test().clone(); + relative_to.push("e-$ èрт🚒♞中片-j"); + + let actual = canonicalize_with("🚒.txt", relative_to).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("e-$ èрт🚒♞中片-j/🚒.txt"); + + assert_eq!(actual, expected); + }); +} + +#[ignore] +#[test] +fn canonicalize_non_utf8_path_relative_to_non_utf8_path_with_spaces() { + // TODO +} + +#[test] +fn canonicalize_absolute_path_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let mut absolute_path = dirs.test().clone(); + absolute_path.push("spam.txt"); + + let actual = canonicalize_with(&absolute_path, "non/existent/directory") + .expect("Failed to canonicalize"); + let expected = absolute_path; + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_dot() { + let cwd = std::env::current_dir().expect("Could not get current directory"); + + let actual = canonicalize_with(".", cwd).expect("Failed to canonicalize"); + let expected = std::env::current_dir().expect("Could not get current directory"); + + assert_eq!(actual, expected); +} + +#[test] +fn canonicalize_many_dots() { + let cwd = std::env::current_dir().expect("Could not get current directory"); + + let actual = + canonicalize_with("././/.//////./././//.///", cwd).expect("Failed to canonicalize"); + let expected = std::env::current_dir().expect("Could not get current directory"); + + assert_eq!(actual, expected); +} + +#[test] +fn canonicalize_path_with_dot_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with("./spam.txt", dirs.test()).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_path_with_many_dots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with("././/.//////./././//.////spam.txt", dirs.test()) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_double_dot() { + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = canonicalize_with("..", &cwd).expect("Failed to canonicalize"); + let expected = cwd + .parent() + .expect("Could not get parent of current directory"); + + assert_eq!(actual, expected); +} + +#[test] +fn canonicalize_path_with_double_dot_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = + canonicalize_with("foo/../spam.txt", dirs.test()).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_path_with_many_double_dots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar/baz"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with("foo/bar/baz/../../../spam.txt", dirs.test()) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_ndots() { + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = canonicalize_with("...", &cwd).expect("Failed to canonicalize"); + let expected = cwd + .parent() + .expect("Could not get parent of current directory") + .parent() + .expect("Could not get parent of a parent of current directory"); + + assert_eq!(actual, expected); +} + +#[test] +fn canonicalize_path_with_3_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = + canonicalize_with("foo/bar/.../spam.txt", dirs.test()).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_path_with_many_3_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar/baz/eggs/sausage/bacon"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with( + "foo/bar/baz/eggs/sausage/bacon/.../.../.../spam.txt", + dirs.test(), + ) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_path_with_4_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar/baz"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with("foo/bar/baz/..../spam.txt", dirs.test()) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_path_with_many_4_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar/baz/eggs/sausage/bacon"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = canonicalize_with( + "foo/bar/baz/eggs/sausage/bacon/..../..../spam.txt", + dirs.test(), + ) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_path_with_way_too_many_dots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar/baz/eggs/sausage/bacon/vikings"); + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let mut relative_to = dirs.test().clone(); + relative_to.push("foo/bar/baz/eggs/sausage/bacon/vikings"); + + let actual = canonicalize_with("././..////././...///././.....///spam.txt", relative_to) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_unicode_path_with_way_too_many_dots_relative_to_unicode_path_with_spaces() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/áčěéí +šř=é/baz/eggs/e-$ èрт🚒♞中片-j/bacon/öäöä öäöä"); + sandbox.with_files(vec![EmptyFile("🚒.txt")]); + + let mut relative_to = dirs.test().clone(); + relative_to.push("foo/áčěéí +šř=é/baz/eggs/e-$ èрт🚒♞中片-j/bacon/öäöä öäöä"); + + let actual = canonicalize_with("././..////././...///././.....///🚒.txt", relative_to) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("🚒.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_tilde() { + let tilde_path = "~"; + + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = canonicalize_with(tilde_path, cwd).expect("Failed to canonicalize"); + + assert!(actual.is_absolute()); + assert!(!actual.starts_with("~")); +} + +#[test] +fn canonicalize_tilde_relative_to() { + let tilde_path = "~"; + + let actual = + canonicalize_with(tilde_path, "non/existent/path").expect("Failed to canonicalize"); + + assert!(actual.is_absolute()); + assert!(!actual.starts_with("~")); +} + +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn canonicalize_symlink() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + sandbox.symlink("spam.txt", "link_to_spam.txt"); + + let mut symlink_path = dirs.test().clone(); + symlink_path.push("link_to_spam.txt"); + + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = canonicalize_with(symlink_path, cwd).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn canonicalize_symlink_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + sandbox.symlink("spam.txt", "link_to_spam.txt"); + + let actual = + canonicalize_with("link_to_spam.txt", dirs.test()).expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[cfg(not(target_arch = "wasm32"))] +#[cfg(not(windows))] // seems like Windows symlink requires existing file or dir +#[test] +fn canonicalize_symlink_loop_relative_to_should_fail() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + // sandbox.with_files(vec![EmptyFile("spam.txt")]); + sandbox.symlink("spam.txt", "link_to_spam.txt"); + sandbox.symlink("link_to_spam.txt", "spam.txt"); + + let actual = canonicalize_with("link_to_spam.txt", dirs.test()); + + assert!(actual.is_err()); + }); +} + +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn canonicalize_nested_symlink_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + sandbox.symlink("spam.txt", "link_to_spam.txt"); + sandbox.symlink("link_to_spam.txt", "link_to_link_to_spam.txt"); + + let actual = canonicalize_with("link_to_link_to_spam.txt", dirs.test()) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[cfg(not(target_arch = "wasm32"))] +#[test] +fn canonicalize_nested_symlink_within_symlink_dir_relative_to() { + Playground::setup("nu_path_test_1", |dirs, sandbox| { + sandbox.mkdir("foo/bar/baz"); + sandbox.with_files(vec![EmptyFile("foo/bar/baz/spam.txt")]); + sandbox.symlink("foo/bar/baz/spam.txt", "foo/bar/link_to_spam.txt"); + sandbox.symlink("foo/bar/link_to_spam.txt", "foo/link_to_link_to_spam.txt"); + sandbox.symlink("foo", "link_to_foo"); + + let actual = canonicalize_with("link_to_foo/link_to_link_to_spam.txt", dirs.test()) + .expect("Failed to canonicalize"); + let mut expected = dirs.test().clone(); + expected.push("foo/bar/baz/spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn canonicalize_should_fail() { + let path = Path::new("/foo/bar/baz"); // hopefully, this path does not exist + + let cwd = std::env::current_dir().expect("Could not get current directory"); + assert!(canonicalize_with(path, cwd).is_err()); +} + +#[test] +fn canonicalize_with_should_fail() { + let relative_to = "/foo"; + let path = "bar/baz"; + + assert!(canonicalize_with(path, relative_to).is_err()); +} diff --git a/tests/path/expand_path.rs b/tests/path/expand_path.rs new file mode 100644 index 0000000000..24f4eeea9d --- /dev/null +++ b/tests/path/expand_path.rs @@ -0,0 +1,246 @@ +use std::path::PathBuf; + +use nu_test_support::playground::Playground; + +use nu_path::expand_path_with; + +#[cfg(not(windows))] +#[test] +fn expand_path_with_and_without_relative() { + let relative_to = "/foo/bar"; + let path = "../.."; + let full_path = "/foo/bar/../.."; + + let cwd = std::env::current_dir().expect("Could not get current directory"); + assert_eq!( + expand_path_with(full_path, cwd), + expand_path_with(path, relative_to), + ); +} + +#[test] +fn expand_path_with_relative() { + let relative_to = "/foo/bar"; + let path = "../.."; + + assert_eq!(PathBuf::from("/"), expand_path_with(path, relative_to),); +} + +#[cfg(not(windows))] +#[test] +fn expand_path_no_change() { + let path = "/foo/bar"; + + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = expand_path_with(&path, cwd); + + assert_eq!(actual, PathBuf::from(path)); +} + +#[test] +fn expand_unicode_path_no_change() { + Playground::setup("nu_path_test_1", |dirs, _| { + let mut spam = dirs.test().clone(); + spam.push("🚒.txt"); + + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = expand_path_with(spam, cwd); + let mut expected = dirs.test().clone(); + expected.push("🚒.txt"); + + assert_eq!(actual, expected); + }); +} + +#[ignore] +#[test] +fn expand_non_utf8_path() { + // TODO +} + +#[test] +fn expand_path_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_unicode_path_relative_to_unicode_path_with_spaces() { + Playground::setup("nu_path_test_1", |dirs, _| { + let mut relative_to = dirs.test().clone(); + relative_to.push("e-$ èрт🚒♞中片-j"); + + let actual = expand_path_with("🚒.txt", relative_to); + let mut expected = dirs.test().clone(); + expected.push("e-$ èрт🚒♞中片-j/🚒.txt"); + + assert_eq!(actual, expected); + }); +} + +#[ignore] +#[test] +fn expand_non_utf8_path_relative_to_non_utf8_path_with_spaces() { + // TODO +} + +#[test] +fn expand_absolute_path_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let mut absolute_path = dirs.test().clone(); + absolute_path.push("spam.txt"); + + let actual = expand_path_with(&absolute_path, "non/existent/directory"); + let expected = absolute_path; + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_dot_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("./spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_many_dots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("././/.//////./././//.////spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_double_dot_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("foo/../spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_many_double_dots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("foo/bar/baz/../../../spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_3_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("foo/bar/.../spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_many_3_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with( + "foo/bar/baz/eggs/sausage/bacon/.../.../.../spam.txt", + dirs.test(), + ); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_4_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with("foo/bar/baz/..../spam.txt", dirs.test()); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_many_4_ndots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let actual = expand_path_with( + "foo/bar/baz/eggs/sausage/bacon/..../..../spam.txt", + dirs.test(), + ); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_with_way_too_many_dots_relative_to() { + Playground::setup("nu_path_test_1", |dirs, _| { + let mut relative_to = dirs.test().clone(); + relative_to.push("foo/bar/baz/eggs/sausage/bacon/vikings"); + + let actual = expand_path_with("././..////././...///././.....///spam.txt", relative_to); + let mut expected = dirs.test().clone(); + expected.push("spam.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_unicode_path_with_way_too_many_dots_relative_to_unicode_path_with_spaces() { + Playground::setup("nu_path_test_1", |dirs, _| { + let mut relative_to = dirs.test().clone(); + relative_to.push("foo/áčěéí +šř=é/baz/eggs/e-$ èрт🚒♞中片-j/bacon/öäöä öäöä"); + + let actual = expand_path_with("././..////././...///././.....///🚒.txt", relative_to); + let mut expected = dirs.test().clone(); + expected.push("🚒.txt"); + + assert_eq!(actual, expected); + }); +} + +#[test] +fn expand_path_tilde() { + let tilde_path = "~"; + + let cwd = std::env::current_dir().expect("Could not get current directory"); + let actual = expand_path_with(tilde_path, cwd); + + assert!(actual.is_absolute()); + assert!(!actual.starts_with("~")); +} + +#[test] +fn expand_path_tilde_relative_to() { + let tilde_path = "~"; + + let actual = expand_path_with(tilde_path, "non/existent/path"); + + assert!(actual.is_absolute()); + assert!(!actual.starts_with("~")); +} diff --git a/tests/path/mod.rs b/tests/path/mod.rs new file mode 100644 index 0000000000..2b679c72b6 --- /dev/null +++ b/tests/path/mod.rs @@ -0,0 +1,2 @@ +mod canonicalize; +mod expand_path; diff --git a/tests/plugins/core_inc.rs b/tests/plugins/core_inc.rs new file mode 100644 index 0000000000..af7e261434 --- /dev/null +++ b/tests/plugins/core_inc.rs @@ -0,0 +1,135 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::nu_with_plugins; +use nu_test_support::playground::Playground; + +#[test] +fn can_only_apply_one() { + let actual = nu_with_plugins!( + cwd: "tests/fixtures/formats", + "open cargo_sample.toml | first 1 | inc package.version --major --minor" + ); + + assert!(actual + .err + .contains("Usage: inc field [--major|--minor|--patch]")); +} + +#[test] +fn by_one_with_field_passed() { + Playground::setup("plugin_inc_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + edition = "2018" + "#, + )]); + + let actual = nu_with_plugins!( + cwd: dirs.test(), + "open sample.toml | inc package.edition | get package.edition" + ); + + assert_eq!(actual.out, "2019"); + }) +} + +#[test] +fn by_one_with_no_field_passed() { + Playground::setup("plugin_inc_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + contributors = "2" + "#, + )]); + + let actual = nu_with_plugins!( + cwd: dirs.test(), + "open sample.toml | get package.contributors | inc" + ); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn semversion_major_inc() { + Playground::setup("plugin_inc_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + version = "0.1.3" + "#, + )]); + + let actual = nu_with_plugins!( + cwd: dirs.test(), + "open sample.toml | inc package.version -M | get package.version" + ); + + assert_eq!(actual.out, "1.0.0"); + }) +} + +#[test] +fn semversion_minor_inc() { + Playground::setup("plugin_inc_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + version = "0.1.3" + "#, + )]); + + let actual = nu_with_plugins!( + cwd: dirs.test(), + "open sample.toml | inc package.version --minor | get package.version" + ); + + assert_eq!(actual.out, "0.2.0"); + }) +} + +#[test] +fn semversion_patch_inc() { + Playground::setup("plugin_inc_test_5", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + version = "0.1.3" + "#, + )]); + + let actual = nu_with_plugins!( + cwd: dirs.test(), + "open sample.toml | inc package.version --patch | get package.version" + ); + + assert_eq!(actual.out, "0.1.4"); + }) +} + +#[test] +fn semversion_without_passing_field() { + Playground::setup("plugin_inc_test_6", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + version = "0.1.3" + "#, + )]); + + let actual = nu_with_plugins!( + cwd: dirs.test(), + "open sample.toml | get package.version | inc --patch" + ); + + assert_eq!(actual.out, "0.1.4"); + }) +} diff --git a/tests/plugins/mod.rs b/tests/plugins/mod.rs new file mode 100644 index 0000000000..23310d10ba --- /dev/null +++ b/tests/plugins/mod.rs @@ -0,0 +1,2 @@ +#[cfg(features = "inc")] +mod core_inc; diff --git a/tests/shell/environment/configuration.rs b/tests/shell/environment/configuration.rs new file mode 100644 index 0000000000..d44dd7e750 --- /dev/null +++ b/tests/shell/environment/configuration.rs @@ -0,0 +1,142 @@ +use nu_test_support::fs::{file_contents, Stub::FileWithContent}; +use nu_test_support::fs::{AbsolutePath, DisplayPath}; +use nu_test_support::pipeline as input; +use nu_test_support::playground::{says, Executable, Playground}; + +use hamcrest2::assert_that; +use hamcrest2::prelude::*; + +#[test] +fn clears_the_configuration() { + Playground::setup("config_clear_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + pivot_mode = "arepas" + "#, + )]); + + assert!(nu.pipeline("config clear").execute().is_ok()); + assert!(file_contents(&file).is_empty()); + }); +} + +#[test] +fn retrieves_config_values() { + Playground::setup("config_get_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [arepa] + colors = ["yellow", "white"] + "#, + )]); + + assert_that!( + nu.pipeline("config get arepa.colors.0"), + says().stdout("yellow") + ); + }) +} + +#[test] +fn sets_a_config_value() { + Playground::setup("config_set_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [nu] + meal = "taco" + "#, + )]); + + assert!(nu.pipeline("config set nu.meal 'arepa'").execute().is_ok()); + + assert_that!(nu.pipeline("config get nu.meal"), says().stdout("arepa")); + }) +} + +#[test] +fn sets_config_values_into_one_property() { + Playground::setup("config_set_into_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + "#, + )]); + + assert!(nu + .pipeline(&input( + r#" + echo ["amarillo", "blanco"] + | config set_into arepa_colors + "#, + )) + .execute() + .is_ok()); + + assert_that!( + nu.pipeline("config get arepa_colors.1"), + says().stdout("blanco") + ); + }) +} + +#[test] +fn config_path() { + Playground::setup("config_path_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + "#, + )]); + + assert_that!( + nu.pipeline("config path"), + says().stdout(&file.display_path()) + ); + }) +} + +#[test] +fn removes_config_values() { + Playground::setup("config_remove_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + "#, + )]); + + assert!(nu + .pipeline("config remove skip_welcome_message") + .execute() + .is_ok()); + assert!(file_contents(&file).is_empty()); + }) +} diff --git a/tests/shell/environment/env.rs b/tests/shell/environment/env.rs new file mode 100644 index 0000000000..2d2cb51fed --- /dev/null +++ b/tests/shell/environment/env.rs @@ -0,0 +1,107 @@ +use super::support::Trusted; + +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +use serial_test::serial; + +#[test] +fn env_shorthand() { + let actual = nu!(cwd: ".", r#" + FOO=bar echo $env.FOO + "#); + assert_eq!(actual.out, "bar"); +} + +#[test] +fn env_shorthand_with_equals() { + let actual = nu!(cwd: ".", r#" + RUST_LOG=my_module=info $env.RUST_LOG + "#); + assert_eq!(actual.out, "my_module=info"); +} + +#[test] +fn env_shorthand_with_comma_equals() { + let actual = nu!(cwd: ".", r#" + RUST_LOG=info,my_module=info $env.RUST_LOG + "#); + assert_eq!(actual.out, "info,my_module=info"); +} + +#[test] +fn env_shorthand_with_comma_colons_equals() { + let actual = nu!(cwd: ".", r#" + RUST_LOG=info,my_module=info,lib_crate::lib_mod=trace $env.RUST_LOG + "#); + assert_eq!(actual.out, "info,my_module=info,lib_crate::lib_mod=trace"); +} + +#[test] +fn env_shorthand_multi_second_with_comma_colons_equals() { + let actual = nu!(cwd: ".", r#" + FOO=bar RUST_LOG=info,my_module=info,lib_crate::lib_mod=trace $env.FOO + $env.RUST_LOG + "#); + assert_eq!( + actual.out, + "barinfo,my_module=info,lib_crate::lib_mod=trace" + ); +} + +#[test] +fn env_shorthand_multi_first_with_comma_colons_equals() { + let actual = nu!(cwd: ".", r#" + RUST_LOG=info,my_module=info,lib_crate::lib_mod=trace FOO=bar $env.FOO + $env.RUST_LOG + "#); + assert_eq!( + actual.out, + "barinfo,my_module=info,lib_crate::lib_mod=trace" + ); +} + +#[test] +fn env_shorthand_multi() { + let actual = nu!(cwd: ".", r#" + FOO=bar BAR=baz $env.FOO + $env.BAR + "#); + assert_eq!(actual.out, "barbaz"); +} + +#[test] +fn passes_let_env_env_var_to_external_process() { + let actual = nu!(cwd: ".", r#" + let-env FOO = foo + nu --testbin echo_env FOO + "#); + assert_eq!(actual.out, "foo"); +} + +#[test] +fn passes_with_env_env_var_to_external_process() { + let actual = nu!(cwd: ".", r#" + with-env [FOO foo] {nu --testbin echo_env FOO} + "#); + assert_eq!(actual.out, "foo"); +} + +#[test] +#[serial] +fn passes_env_from_local_cfg_to_external_process() { + Playground::setup("autoenv_dir", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + r#"[env] + FOO = "foo" + "#, + )]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), r#" + nu --testbin echo_env FOO + "#) + }); + + assert_eq!(actual.out, "foo"); + }) +} diff --git a/tests/shell/environment/in_sync.rs b/tests/shell/environment/in_sync.rs new file mode 100644 index 0000000000..353507644e --- /dev/null +++ b/tests/shell/environment/in_sync.rs @@ -0,0 +1,116 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::fs::{AbsolutePath, DisplayPath}; +use nu_test_support::playground::{says, Playground}; + +use std::path::PathBuf; + +use hamcrest2::assert_that; +use hamcrest2::prelude::*; + +#[test] +fn setting_environment_value_to_configuration_should_pick_up_into_in_memory_environment_on_runtime() +{ + Playground::setup("environment_syncing_test_1", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [env] + SHELL = "/local/nu" + "#, + )]); + + assert_that!( + nu.pipeline("config set env.USER NUNO | ignore") + .and_then("echo $env.USER"), + says().stdout("NUNO") + ); + }); +} + +#[test] +fn inherited_environment_values_not_present_in_configuration_should_pick_up_into_in_memory_environment( +) { + Playground::setup("environment_syncing_test_2", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [env] + SHELL = "/local/nu" + "#, + )]) + .with_config(&file) + .with_env("USER", "NUNO"); + + assert_that!(nu.pipeline("echo $env.USER"), says().stdout("NUNO")); + }); +} + +#[test] +fn environment_values_present_in_configuration_overwrites_inherited_environment_values() { + Playground::setup("environment_syncing_test_3", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + [env] + SHELL = "/usr/bin/you_already_made_the_nu_choice" + "#, + )]) + .with_config(&file) + .with_env("SHELL", "/usr/bin/sh"); + + assert_that!( + nu.pipeline("echo $env.SHELL"), + says().stdout("/usr/bin/you_already_made_the_nu_choice") + ); + }); +} + +#[test] +fn inherited_environment_path_values_not_present_in_configuration_should_pick_up_into_in_memory_environment( +) { + Playground::setup("environment_syncing_test_4", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + + let expected_paths = vec![ + PathBuf::from("/Users/andresrobalino/.volta/bin"), + PathBuf::from("/Users/mosqueteros/bin"), + PathBuf::from("/path/to/be/added"), + ] + .iter() + .map(|p| p.display_path()) + .collect::>() + .join("-"); + + nu.with_files(vec![FileWithContent( + "config.toml", + r#" + skip_welcome_message = true + + path = ["/Users/andresrobalino/.volta/bin", "/Users/mosqueteros/bin"] + "#, + )]) + .with_config(&file) + .with_env( + nu_test_support::NATIVE_PATH_ENV_VAR, + &PathBuf::from("/path/to/be/added").display_path(), + ); + + assert_that!( + nu.pipeline("echo $nu.path | str collect '-'"), + says().stdout(&expected_paths) + ); + }); +} diff --git a/tests/shell/environment/mod.rs b/tests/shell/environment/mod.rs new file mode 100644 index 0000000000..f505dcbf35 --- /dev/null +++ b/tests/shell/environment/mod.rs @@ -0,0 +1,22 @@ +mod configuration; +mod env; +mod in_sync; +mod nu_env; + +pub mod support { + use nu_test_support::{nu, playground::*, Outcome}; + + pub struct Trusted; + + impl Trusted { + pub fn in_path(dirs: &Dirs, block: impl FnOnce() -> Outcome) -> Outcome { + let for_env_manifest = dirs.test().to_string_lossy(); + + nu!(cwd: dirs.root(), format!("autoenv trust \"{}\"", for_env_manifest)); + let out = block(); + nu!(cwd: dirs.root(), format!("autoenv untrust \"{}\"", for_env_manifest)); + + out + } + } +} diff --git a/tests/shell/environment/nu_env.rs b/tests/shell/environment/nu_env.rs new file mode 100644 index 0000000000..14cc3b1f29 --- /dev/null +++ b/tests/shell/environment/nu_env.rs @@ -0,0 +1,672 @@ +use super::support::Trusted; + +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +use serial_test::serial; + +const SCRIPTS: &str = r#"startup = ["touch hello.txt"] + on_exit = ["touch bye.txt"]"#; + +#[test] +#[serial] +fn picks_up_env_keys_when_entering_trusted_directory() { + Playground::setup("autoenv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + + [scriptvars] + myscript = "echo myval" + "# + ), + )]); + + let expected = "testvalue"; + + let actual = Trusted::in_path(&dirs, || nu!(cwd: dirs.test(), "echo $env.testkey")); + + assert_eq!(actual.out, expected); + }) +} + +#[cfg(feature = "which-support")] +#[test] +#[serial] +fn picks_up_and_lets_go_env_keys_when_entering_trusted_directory_with_implied_cd() { + use nu_test_support::fs::Stub::FileWithContent; + Playground::setup("autoenv_test", |dirs, sandbox| { + sandbox.mkdir("foo"); + sandbox.mkdir("foo/bar"); + sandbox.with_files(vec![ + FileWithContent( + "foo/.nu-env", + r#"[env] + testkey = "testvalue" + "#, + ), + FileWithContent( + "foo/bar/.nu-env", + r#" + [env] + bar = "true" + "#, + ), + ]); + let actual = nu!( + cwd: dirs.test(), + r#" + do {autoenv trust -q foo ; = $nothing } + foo + echo $env.testkey"# + ); + assert_eq!(actual.out, "testvalue"); + //Assert testkey is gone when leaving foo + let actual = nu!( + cwd: dirs.test(), + r#" + do {autoenv trust -q foo; = $nothing } ; + foo + .. + echo $env.testkey + "# + ); + assert!(actual.err.contains("Unknown")); + //Assert testkey is present also when jumping over foo + let actual = nu!( + cwd: dirs.test(), + r#" + do {autoenv trust -q foo; = $nothing } ; + do {autoenv trust -q foo/bar; = $nothing } ; + foo/bar + echo $env.testkey + echo $env.bar + "# + ); + assert_eq!(actual.out, "testvaluetrue"); + //Assert bar removed after leaving bar + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; + foo/bar + ../.. + echo $env.bar"# + ); + assert!(actual.err.contains("Unknown")); + }); +} + +#[test] +#[serial] +#[ignore] +fn picks_up_script_vars_when_entering_trusted_directory() { + Playground::setup("autoenv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + + [scriptvars] + myscript = "echo myval" + "# + ), + )]); + + let expected = "myval"; + + let actual = Trusted::in_path(&dirs, || nu!(cwd: dirs.test(), "echo $env.myscript")); + + // scriptvars are not supported + // and why is myval expected when myscript is "echo myval" + assert_eq!(actual.out, expected); + }) +} + +#[test] +#[serial] +fn picks_up_env_keys_when_entering_trusted_directory_indirectly() { + Playground::setup("autoenv_test_3", |dirs, sandbox| { + sandbox.mkdir("crates"); + sandbox.with_files(vec![FileWithContent( + ".nu-env", + r#"[env] + nu-ver = "0.30.0" "#, + )]); + + let expected = "0.30.0"; + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test().join("crates"), r#" + cd ../../autoenv_test_3 + echo $env.nu-ver + "#) + }); + + assert_eq!(actual.out, expected); + }) +} + +#[test] +#[serial] +fn entering_a_trusted_directory_runs_entry_scripts() { + Playground::setup("autoenv_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + "# + ), + )]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), pipeline(r#" + ls + | where name == "hello.txt" + | get name + "#)) + }); + + assert_eq!(actual.out, "hello.txt"); + }) +} + +#[test] +#[serial] +fn leaving_a_trusted_directory_runs_exit_scripts() { + Playground::setup("autoenv_test_5", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + + [scriptvars] + myscript = "echo myval" + "# + ), + )]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), r#" + cd .. + ls autoenv_test_5 | get name | path basename | where $it == "bye.txt" + "#) + }); + + assert_eq!(actual.out, "bye.txt"); + }) +} + +#[test] +#[serial] +fn entry_scripts_are_called_when_revisiting_a_trusted_directory() { + Playground::setup("autoenv_test_6", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + + [scriptvars] + myscript = "echo myval" + "# + ), + )]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), r#" + do { rm hello.txt | ignore } ; # Silence file deletion message from output + cd .. + cd autoenv_test_6 + ls | where name == "hello.txt" | get name + "#) + }); + + assert_eq!(actual.out, "hello.txt"); + }) +} + +#[test] +#[serial] +fn given_a_trusted_directory_with_entry_scripts_when_entering_a_subdirectory_entry_scripts_are_not_called( +) { + Playground::setup("autoenv_test_7", |dirs, sandbox| { + sandbox.mkdir("time_to_cook_arepas"); + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + + [scriptvars] + myscript = "echo myval" + "# + ), + )]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), r#" + cd time_to_cook_arepas + ls | where name == "hello.txt" | length + "#) + }); + + assert_eq!(actual.out, "0"); + }) +} + +#[test] +#[serial] +fn given_a_trusted_directory_with_exit_scripts_when_entering_a_subdirectory_exit_scripts_are_not_called( +) { + Playground::setup("autoenv_test_8", |dirs, sandbox| { + sandbox.mkdir("time_to_cook_arepas"); + sandbox.with_files(vec![FileWithContent( + ".nu-env", + &format!( + "{}\n{}", + SCRIPTS, + r#"[env] + testkey = "testvalue" + + [scriptvars] + myscript = "echo myval" + "# + ), + )]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), r#" + cd time_to_cook_arepas + ls | where name == "bye.txt" | length + "#) + }); + + assert_eq!(actual.out, "0"); + }) +} + +#[test] +#[serial] +fn given_a_hierachy_of_trusted_directories_when_entering_in_any_nested_ones_should_carry_over_variables_set_from_the_root( +) { + Playground::setup("autoenv_test_9", |dirs, sandbox| { + sandbox.mkdir("nu_plugin_rb"); + sandbox.with_files(vec![ + FileWithContent( + ".nu-env", + r#"[env] + organization = "nushell""#, + ), + FileWithContent( + "nu_plugin_rb/.nu-env", + r#"[env] + language = "Ruby""#, + ), + ]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test().parent().unwrap(), r#" + do { autoenv trust -q autoenv_test_9/nu_plugin_rb ; = $nothing } # Silence autoenv trust -q message from output + cd autoenv_test_9/nu_plugin_rb + echo $env.organization + "#) + }); + + assert_eq!(actual.out, "nushell"); + }) +} + +#[test] +#[serial] +fn given_a_hierachy_of_trusted_directories_nested_ones_should_overwrite_variables_from_parent_directories( +) { + Playground::setup("autoenv_test_10", |dirs, sandbox| { + sandbox.mkdir("nu_plugin_rb"); + sandbox.with_files(vec![ + FileWithContent( + ".nu-env", + r#"[env] + organization = "nushell""#, + ), + FileWithContent( + "nu_plugin_rb/.nu-env", + r#"[env] + organization = "Andrab""#, + ), + ]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test().parent().unwrap(), r#" + do { autoenv trust -q autoenv_test_10/nu_plugin_rb ; = $nothing } # Silence autoenv trust -q message from output + cd autoenv_test_10/nu_plugin_rb + echo $env.organization + "#) + }); + + assert_eq!(actual.out, "Andrab"); + }) +} + +#[test] +#[serial] +#[cfg(not(windows))] //TODO figure out why this test doesn't work on windows +fn local_config_should_not_be_added_when_running_scripts() { + Playground::setup("autoenv_test_10", |dirs, sandbox| { + sandbox.mkdir("foo"); + sandbox.with_files(vec![ + FileWithContent( + ".nu-env", + r#"[env] + organization = "nu""#, + ), + FileWithContent( + "foo/.nu-env", + r#"[env] + organization = "foo""#, + ), + FileWithContent( + "script.nu", + r#"cd foo + echo $env.organization"#, + ), + ]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test(), r#" + do { autoenv trust -q foo } # Silence autoenv trust message from output + nu script.nu + "#) + }); + + assert_eq!(actual.out, "nu"); + }) +} +#[test] +#[serial] +fn given_a_hierachy_of_trusted_directories_going_back_restores_overwritten_variables() { + Playground::setup("autoenv_test_11", |dirs, sandbox| { + sandbox.mkdir("nu_plugin_rb"); + sandbox.with_files(vec![ + FileWithContent( + ".nu-env", + r#"[env] + organization = "nushell""#, + ), + FileWithContent( + "nu_plugin_rb/.nu-env", + r#"[env] + organization = "Andrab""#, + ), + ]); + + let actual = Trusted::in_path(&dirs, || { + nu!(cwd: dirs.test().parent().unwrap(), r#" + do { autoenv trust -q autoenv_test_11/nu_plugin_rb } # Silence autoenv trust message from output + cd autoenv_test_11 + cd nu_plugin_rb + do { rm ../.nu-env | ignore } # By deleting the root nu-env we have guarantees that the variable gets restored (not by autoenv when re-entering) + cd .. + echo $env.organization + "#) + }); + + assert_eq!(actual.out, "nushell"); + }) +} + +#[cfg(feature = "which-support")] +#[test] +#[serial] +fn local_config_env_var_present_and_removed_correctly() { + use nu_test_support::fs::Stub::FileWithContent; + Playground::setup("autoenv_test", |dirs, sandbox| { + sandbox.mkdir("foo"); + sandbox.mkdir("foo/bar"); + sandbox.with_files(vec![FileWithContent( + "foo/.nu-env", + r#"[env] + testkey = "testvalue" + "#, + )]); + //Assert testkey is not present before entering directory + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; + echo $env.testkey"# + ); + assert!(actual.err.contains("Unknown")); + //Assert testkey is present in foo + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; cd foo + echo $env.testkey"# + ); + assert_eq!(actual.out, "testvalue"); + //Assert testkey is present also in subdirectories + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; cd foo + cd bar + echo $env.testkey"# + ); + assert_eq!(actual.out, "testvalue"); + //Assert testkey is present also when jumping over foo + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; cd foo/bar + echo $env.testkey"# + ); + assert_eq!(actual.out, "testvalue"); + //Assert testkey removed after leaving foo + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; cd foo + cd .. + echo $env.testkey"# + ); + assert!(actual.err.contains("Unknown")); + }); +} + +#[cfg(feature = "which-support")] +#[test] +#[serial] +fn local_config_env_var_gets_overwritten() { + use nu_test_support::fs::Stub::FileWithContent; + Playground::setup("autoenv_test", |dirs, sandbox| { + sandbox.mkdir("foo"); + sandbox.mkdir("foo/bar"); + sandbox.with_files(vec![ + FileWithContent( + "foo/.nu-env", + r#"[env] + overwrite_me = "foo" + "#, + ), + FileWithContent( + "foo/bar/.nu-env", + r#"[env] + overwrite_me = "bar" + "#, + ), + ]); + //Assert overwrite_me is not present before entering directory + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; + echo $env.overwrite_me"# + ); + assert!(actual.err.contains("Unknown")); + //Assert overwrite_me is foo in foo + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; cd foo + echo $env.overwrite_me"# + ); + assert_eq!(actual.out, "foo"); + //Assert overwrite_me is bar in bar + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + autoenv trust -q foo/bar + cd foo + cd bar + echo $env.overwrite_me"# + ); + assert_eq!(actual.out, "bar"); + //Assert overwrite_me is present also when jumping over foo + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; autoenv trust -q foo/bar; cd foo/bar + echo $env.overwrite_me + "# + ); + assert_eq!(actual.out, "bar"); + //Assert overwrite_me removed after leaving bar + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo; autoenv trust -q foo/bar; cd foo + cd bar + cd .. + echo $env.overwrite_me"# + ); + assert_eq!(actual.out, "foo"); + }); +} + +#[cfg(feature = "which-support")] +#[test] +#[serial] +fn autoenv_test_entry_scripts() { + use nu_test_support::fs::Stub::FileWithContent; + Playground::setup("autoenv_test", |dirs, sandbox| { + sandbox.mkdir("foo/bar"); + + // Windows uses a different command to create an empty file so we need to have different content on windows. + let nu_env = if cfg!(target_os = "windows") { + r#"startup = ["echo nul > hello.txt"]"# + } else { + r#"startup = ["touch hello.txt"]"# + }; + + sandbox.with_files(vec![FileWithContent("foo/.nu-env", nu_env)]); + + // Make sure entryscript is run when entering directory + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + cd foo + ls | where name == "hello.txt" | get name"# + ); + assert!(actual.out.contains("hello.txt")); + + // Make sure entry scripts are also run when jumping over directory + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + cd foo/bar + ls .. | where name == "../hello.txt" | get name"# + ); + assert!(actual.out.contains("hello.txt")); + + // Entryscripts should not run after changing to a subdirectory. + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + cd foo + rm hello.txt + cd bar + ls .. | where name == "../hello.txt" | length"# + ); + assert!(actual.out.contains('0')); + }); +} + +#[cfg(feature = "which-support")] +#[test] +#[serial] +fn autoenv_test_exit_scripts() { + use nu_test_support::fs::Stub::FileWithContent; + Playground::setup("autoenv_test", |dirs, sandbox| { + sandbox.mkdir("foo/bar"); + + // Windows uses a different command to create an empty file so we need to have different content on windows. + let nu_env = r#"on_exit = ["touch bye.txt"]"#; + + sandbox.with_files(vec![FileWithContent("foo/.nu-env", nu_env)]); + + // Make sure exitscript is run + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + cd foo + cd .. + ls foo | where name =~ "bye.txt" | length + rm foo/bye.txt | ignore; cd . + "# + ); + assert_eq!(actual.out, "1"); + + // Entering a subdir should not trigger exitscripts + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + cd foo + cd bar + ls .. | where name =~ "bye.txt" | length"# + ); + assert_eq!(actual.out, "0"); + + // Also run exitscripts when jumping over directory + let actual = nu!( + cwd: dirs.test(), + r#"autoenv trust -q foo + cd foo/bar + cd ../.. + ls foo | where name =~ "bye.txt" | length + rm foo/bye.txt | ignore; cd ."# + ); + assert_eq!(actual.out, "1"); + }); +} + +#[test] +#[serial] +#[cfg(unix)] +fn prepends_path_from_local_config() { + //If this test fails for you, make sure that your environment from which you start nu + //contains some env vars + Playground::setup("autoenv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + ".nu-env", + r#" + path = ["/hi", "/nushell"] + "#, + )]); + + let expected = "[\"/hi\",\"/nushell\","; + + let actual = Trusted::in_path(&dirs, || nu!(cwd: dirs.test(), "echo $nu.path | to json")); + // assert_eq!("", actual.out); + assert!(actual.out.starts_with(expected)); + assert!(actual.out.len() > expected.len()); + }) +} diff --git a/tests/shell/mod.rs b/tests/shell/mod.rs new file mode 100644 index 0000000000..24b62ccd09 --- /dev/null +++ b/tests/shell/mod.rs @@ -0,0 +1,76 @@ +use nu_test_support::fs::AbsolutePath; +use nu_test_support::playground::{says, Playground}; +use nu_test_support::{nu, pipeline}; + +use hamcrest2::assert_that; +use hamcrest2::prelude::*; + +#[cfg(feature = "which-support")] +mod environment; + +mod pipeline; + +//FIXME: jt: this approach may no longer be right for running from startup scripts, needs investigation +#[ignore] +#[test] +fn runs_configuration_startup_commands() { + Playground::setup("init_config_startup_commands_test", |dirs, nu| { + let file = AbsolutePath::new(dirs.config_fixtures().join("startup.toml")); + + nu.with_config(&file); + + assert_that!(nu.pipeline("hello-world"), says().stdout("Nu World")); + }); +} + +//FIXME: jt: we need to focus some fixes on wix as the plugins will differ +#[ignore] +#[test] +fn plugins_are_declared_with_wix() { + let actual = nu!( + cwd: ".", pipeline( + r#" + open Cargo.toml + | get bin.name + | str find-replace "nu_plugin_(extra|core)_(.*)" "nu_plugin_$2" + | drop + | sort-by + | wrap cargo | merge { + open wix/main.wxs --raw | from xml + | get Wix.children.Product.children.0.Directory.children.0 + | where Directory.attributes.Id == "$(var.PlatformProgramFilesFolder)" + | get Directory.children.Directory.children.0 | last + | get Directory.children.Component.children + | each { echo $it | first } + | skip + | where File.attributes.Name =~ "nu_plugin" + | str substring [_, -4] File.attributes.Name + | get File.attributes.Name + | sort-by + | wrap wix + } + | default wix _ + | each { if $it.wix != $it.cargo { 1 } { 0 } } + | math sum + "# + )); + + assert_eq!(actual.out, "0"); +} + +#[test] +#[cfg(not(windows))] +fn do_not_panic_if_broken_pipe() { + // `nu -h | false` + // used to panic with a BrokenPipe error + let child_output = std::process::Command::new("sh") + .arg("-c") + .arg(format!( + "{:?} -h | false", + nu_test_support::fs::executable_path() + )) + .output() + .expect("failed to execute process"); + + assert!(child_output.stderr.is_empty()); +} diff --git a/tests/shell/pipeline/commands/external.rs b/tests/shell/pipeline/commands/external.rs new file mode 100644 index 0000000000..bd6efaded2 --- /dev/null +++ b/tests/shell/pipeline/commands/external.rs @@ -0,0 +1,457 @@ +use nu_test_support::nu; + +#[cfg(feature = "which")] +#[test] +fn shows_error_for_command_not_found() { + let actual = nu!( + cwd: ".", + "ferris_is_not_here.exe" + ); + + assert!(!actual.err.is_empty()); +} + +#[cfg(feature = "which")] +#[test] +fn shows_error_for_command_not_found_in_pipeline() { + let actual = nu!( + cwd: ".", + "ferris_is_not_here.exe | echo done" + ); + + assert!(!actual.err.is_empty()); +} + +#[ignore] // jt: we can't test this using the -c workaround currently +#[cfg(feature = "which")] +#[test] +fn automatically_change_directory() { + use nu_test_support::playground::Playground; + + Playground::setup("cd_test_5_1", |dirs, sandbox| { + sandbox.mkdir("autodir"); + + let actual = nu!( + cwd: dirs.test(), + r#" + autodir + echo (pwd) + "# + ); + + assert!(actual.out.ends_with("autodir")); + }) +} + +// FIXME: jt: we don't currently support autocd in testing +#[ignore] +#[test] +fn automatically_change_directory_with_trailing_slash_and_same_name_as_command() { + use nu_test_support::playground::Playground; + + Playground::setup("cd_test_5_1", |dirs, sandbox| { + sandbox.mkdir("cd"); + + let actual = nu!( + cwd: dirs.test(), + r#" + cd/ + pwd + "# + ); + + assert!(actual.out.ends_with("cd")); + }) +} + +#[test] +fn correctly_escape_external_arguments() { + let actual = nu!(cwd: ".", r#"^echo '$0'"#); + + assert_eq!(actual.out, "$0"); +} + +#[test] +fn execute_binary_in_string() { + let actual = nu!( + cwd: ".", + r#" + let cmd = "echo" + ^$"($cmd)" "$0" + "#); + + assert_eq!(actual.out, "$0"); +} + +//FIXME: jt - this is blocked on https://github.com/nushell/engine-q/issues/875 +#[ignore] +#[test] +fn redirects_custom_command_external() { + let actual = nu!(cwd: ".", r#"def foo [] { nu --testbin cococo foo bar }; foo | str length"#); + + assert_eq!(actual.out, "8"); +} + +mod it_evaluation { + use super::nu; + use nu_test_support::fs::Stub::{EmptyFile, FileWithContent, FileWithContentToBeTrimmed}; + use nu_test_support::{pipeline, playground::Playground}; + + #[test] + fn takes_rows_of_nu_value_strings() { + Playground::setup("it_argument_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("jonathan_likes_cake.txt"), + EmptyFile("andres_likes_arepas.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | sort-by name + | get name + | each { nu --testbin cococo $it | lines } + | get 1 + "# + )); + + assert_eq!(actual.out, "jonathan_likes_cake.txt"); + }) + } + + #[test] + fn takes_rows_of_nu_value_lines() { + Playground::setup("it_argument_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "nu_candies.txt", + r#" + AndrásWithKitKatzz + AndrásWithKitKatz + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nu_candies.txt + | lines + | each { nu --testbin chop $it | lines} + | get 1 + "# + )); + + assert_eq!(actual.out, "AndrásWithKitKat"); + }) + } + + #[test] + fn can_properly_buffer_lines_externally() { + let actual = nu!( + cwd: ".", + r#" + nu --testbin repeater c 8197 | lines | length + "# + ); + + assert_eq!(actual.out, "1"); + } + #[test] + fn supports_fetching_given_a_column_path_to_it() { + Playground::setup("it_argument_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + nu_party_venue = "zion" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | nu --testbin cococo $in.nu_party_venue + "# + )); + + assert_eq!(actual.out, "zion"); + }) + } +} + +mod stdin_evaluation { + use super::nu; + use nu_test_support::pipeline; + + #[test] + fn does_not_panic_with_no_newline_in_stream() { + let actual = nu!( + cwd: ".", + pipeline(r#" + nu --testbin nonu "wheres the nuline?" | length + "# + )); + + assert_eq!(actual.err, ""); + } + + // FIXME: JT: `lines` doesn't currently support this kind of streaming + #[ignore] + #[test] + fn does_not_block_indefinitely() { + let stdout = nu!( + cwd: ".", + pipeline(r#" + ( nu --testbin iecho yes + | nu --testbin chop + | nu --testbin chop + | lines + | first 1 ) + "# + )) + .out; + + assert_eq!(stdout, "y"); + } +} + +mod external_words { + use super::nu; + use nu_test_support::fs::Stub::FileWithContent; + use nu_test_support::{pipeline, playground::Playground}; + #[test] + fn relaxed_external_words() { + let actual = nu!(cwd: ".", r#" + nu --testbin cococo joturner@foo.bar.baz + "#); + + assert_eq!(actual.out, "joturner@foo.bar.baz"); + } + + //FIXME: jt: limitation in testing - can't use single ticks currently + #[ignore] + #[test] + fn no_escaping_for_single_quoted_strings() { + let actual = nu!(cwd: ".", r#" + nu --testbin cococo 'test "things"' + "#); + + assert_eq!(actual.out, "test \"things\""); + } + + #[rstest::rstest] + #[case("sample.toml", r#""sample.toml""#)] + #[case("a sample file.toml", r#""a sample file.toml""#)] + //FIXME: jt: we don't currently support single ticks in tests + //#[case("quote'mark.toml", r#""quote'mark.toml""#)] + #[cfg_attr( + not(target_os = "windows"), + case(r#"quote"mark.toml"#, r#"$"quote(char double_quote)mark.toml""#) + )] + #[cfg_attr(not(target_os = "windows"), case("?mark.toml", r#""?mark.toml""#))] + #[cfg_attr(not(target_os = "windows"), case("*.toml", r#""*.toml""#))] + #[cfg_attr(not(target_os = "windows"), case("*.toml", "*.toml"))] + #[case("$ sign.toml", r#""$ sign.toml""#)] + fn external_arg_with_special_characters(#[case] path: &str, #[case] nu_path_argument: &str) { + Playground::setup("external_arg_with_quotes", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + path, + r#" + nu_party_venue = "zion" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + &format!(r#" + nu --testbin meow {} | from toml | get nu_party_venue + "#, nu_path_argument) + )); + + assert_eq!(actual.out, "zion"); + }) + } +} + +mod nu_commands { + use super::nu; + + #[test] + fn echo_internally_externally() { + let actual = nu!(cwd: ".", r#" + nu -c "echo 'foo'" + "#); + + assert_eq!(actual.out, "foo"); + } +} + +mod nu_script { + use super::nu; + + #[test] + fn run_nu_script() { + let actual = nu!(cwd: "tests/fixtures/formats", r#" + nu script.nu + "#); + + assert_eq!(actual.out, "done"); + } + + #[test] + fn run_nu_script_multiline() { + let actual = nu!(cwd: "tests/fixtures/formats", r#" + nu script_multiline.nu + "#); + + assert_eq!(actual.out, "23"); + } +} + +mod tilde_expansion { + use super::nu; + + #[test] + fn as_home_directory_when_passed_as_argument_and_begins_with_tilde() { + let actual = nu!( + cwd: ".", + r#" + nu --testbin cococo ~ + "# + ); + + assert!(!actual.out.contains('~')); + } + + #[test] + fn does_not_expand_when_passed_as_argument_and_does_not_start_with_tilde() { + let actual = nu!( + cwd: ".", + r#" + nu --testbin cococo "1~1" + "# + ); + + assert_eq!(actual.out, "1~1"); + } +} + +mod external_command_arguments { + use super::nu; + use nu_test_support::fs::Stub::EmptyFile; + use nu_test_support::{pipeline, playground::Playground}; + #[test] + fn expands_table_of_primitives_to_positional_arguments() { + Playground::setup( + "expands_table_of_primitives_to_positional_arguments", + |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("jonathan_likes_cake.txt"), + EmptyFile("andres_likes_arepas.txt"), + EmptyFile("ferris_not_here.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + nu --testbin cococo (ls | get name) + "# + )); + + assert_eq!( + actual.out, + "andres_likes_arepas.txt ferris_not_here.txt jonathan_likes_cake.txt" + ); + }, + ) + } + + #[test] + fn proper_subexpression_paths_in_external_args() { + Playground::setup( + "expands_table_of_primitives_to_positional_arguments", + |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("jonathan_likes_cake.txt"), + EmptyFile("andres_likes_arepas.txt"), + EmptyFile("ferris_not_here.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + nu --testbin cococo (ls | sort-by name | get name).1 + "# + )); + + assert_eq!(actual.out, "ferris_not_here.txt"); + }, + ) + } + + #[cfg(not(windows))] + #[test] + fn string_interpolation_with_an_external_command() { + Playground::setup( + "string_interpolation_with_an_external_command", + |dirs, sandbox| { + sandbox.mkdir("cd"); + + sandbox.with_files(vec![EmptyFile("cd/jt_likes_cake.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + nu --testbin cococo $"(pwd)/cd" + "# + )); + + assert!(actual.out.contains("cd")); + }, + ) + } + + #[cfg(not(windows))] + #[test] + fn semicolons_are_sanitized_before_passing_to_subshell() { + let actual = nu!( + cwd: ".", + "^echo \"a;b\"" + ); + + assert_eq!(actual.out, "a;b"); + } + + #[cfg(not(windows))] + #[test] + fn ampersands_are_sanitized_before_passing_to_subshell() { + let actual = nu!( + cwd: ".", + "^echo \"a&b\"" + ); + + assert_eq!(actual.out, "a&b"); + } + + #[cfg(not(windows))] + #[test] + fn subcommands_are_sanitized_before_passing_to_subshell() { + let actual = nu!( + cwd: ".", + "nu --testbin cococo \"$(ls)\"" + ); + + assert_eq!(actual.out, "$(ls)"); + } + + #[cfg(not(windows))] + #[test] + fn shell_arguments_are_sanitized_even_if_coming_from_other_commands() { + let actual = nu!( + cwd: ".", + "nu --testbin cococo (echo \"a;&$(hello)\")" + ); + + assert_eq!(actual.out, "a;&$(hello)"); + } +} diff --git a/tests/shell/pipeline/commands/internal.rs b/tests/shell/pipeline/commands/internal.rs new file mode 100644 index 0000000000..0766f8ca26 --- /dev/null +++ b/tests/shell/pipeline/commands/internal.rs @@ -0,0 +1,1354 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::nu; +use nu_test_support::pipeline; +use nu_test_support::playground::Playground; + +#[test] +fn takes_rows_of_nu_value_strings_and_pipes_it_to_stdin_of_external() { + Playground::setup("internal_to_external_pipe_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "nu_times.csv", + r#" + name,rusty_luck,origin + Jason,1,Canada + Jonathan,1,New Zealand + Andrés,1,Ecuador + AndKitKatz,1,Estados Unidos + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nu_times.csv + | get origin + | each { ^echo $it | nu --testbin chop | lines } + | get 2 + "# + )); + + // chop will remove the last escaped double quote from \"Estados Unidos\" + assert_eq!(actual.out, "Ecuado"); + }) +} + +#[test] +fn treats_dot_dot_as_path_not_range() { + Playground::setup("dot_dot_dir", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "nu_times.csv", + r#" + name,rusty_luck,origin + Jason,1,Canada + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + mkdir temp; + cd temp; + echo (open ../nu_times.csv).name.0 | table; + cd ..; + rmdir temp + "# + )); + + // chop will remove the last escaped double quote from \"Estados Unidos\" + assert_eq!(actual.out, "Jason"); + }) +} + +#[test] +fn subexpression_properly_redirects() { + let actual = nu!( + cwd: ".", + r#" + echo (nu --testbin cococo "hello") | str collect + "# + ); + + assert_eq!(actual.out, "hello"); +} + +#[test] +fn argument_subexpression() { + let actual = nu!( + cwd: ".", + r#" + echo "foo" | each { echo (echo $it) } + "# + ); + + assert_eq!(actual.out, "foo"); +} + +#[test] +fn subexpression_handles_dot() { + Playground::setup("subexpression_handles_dot", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "nu_times.csv", + r#" + name,rusty_luck,origin + Jason,1,Canada + Jonathan,1,New Zealand + Andrés,1,Ecuador + AndKitKatz,1,Estados Unidos + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo (open nu_times.csv) + | get name + | each { nu --testbin chop $it | lines } + | get 3 + "# + )); + + assert_eq!(actual.out, "AndKitKat"); + }) +} + +#[test] +fn string_interpolation_with_it() { + let actual = nu!( + cwd: ".", + r#" + echo "foo" | each { echo $"($it)" } + "# + ); + + assert_eq!(actual.out, "foo"); +} + +#[test] +fn string_interpolation_with_it_column_path() { + let actual = nu!( + cwd: ".", + r#" + echo [[name]; [sammie]] | each { echo $"($it.name)" } + "# + ); + + assert_eq!(actual.out, "sammie"); +} + +#[test] +fn string_interpolation_shorthand_overlap() { + let actual = nu!( + cwd: ".", + r#" + $"3 + 4 = (3 + 4)" + "# + ); + + assert_eq!(actual.out, "3 + 4 = 7"); +} + +// FIXME: jt - we don't currently have a way to escape the single ticks easily +#[ignore] +#[test] +fn string_interpolation_and_paren() { + let actual = nu!( + cwd: ".", + r#" + $"a paren is ('(')" + "# + ); + + assert_eq!(actual.out, "a paren is ("); +} + +#[test] +fn string_interpolation_with_unicode() { + //カ = U+30AB : KATAKANA LETTER KA + let actual = nu!( + cwd: ".", + r#" + $"カ" + "# + ); + + assert_eq!(actual.out, "カ"); +} + +#[test] +fn run_custom_command() { + let actual = nu!( + cwd: ".", + r#" + def add-me [x y] { $x + $y}; add-me 10 5 + "# + ); + + assert_eq!(actual.out, "15"); +} + +#[test] +fn run_custom_command_with_flag() { + let actual = nu!( + cwd: ".", + r#" + def foo [--bar:number] { if ($bar | empty?) { echo "empty" } else { echo $bar } }; foo --bar 10 + "# + ); + + assert_eq!(actual.out, "10"); +} + +#[test] +fn run_custom_command_with_flag_missing() { + let actual = nu!( + cwd: ".", + r#" + def foo [--bar:number] { if ($bar | empty?) { echo "empty" } else { echo $bar } }; foo + "# + ); + + assert_eq!(actual.out, "empty"); +} + +#[test] +fn run_custom_subcommand() { + let actual = nu!( + cwd: ".", + r#" + def "str double" [x] { echo $x $x | str collect }; str double bob + "# + ); + + assert_eq!(actual.out, "bobbob"); +} + +#[test] +fn run_inner_custom_command() { + let actual = nu!( + cwd: ".", + r#" + def outer [x] { def inner [y] { echo $y }; inner $x }; outer 10 + "# + ); + + assert_eq!(actual.out, "10"); +} + +#[test] +fn run_broken_inner_custom_command() { + let actual = nu!( + cwd: ".", + r#" + def outer [x] { def inner [y] { echo $y }; inner $x }; inner 10 + "# + ); + + assert!(!actual.err.is_empty()); +} + +#[test] +fn run_custom_command_with_rest() { + let actual = nu!( + cwd: ".", + r#" + def rest-me [...rest: string] { echo $rest.1 $rest.0}; rest-me "hello" "world" | to json --raw + "# + ); + + assert_eq!(actual.out, r#"["world","hello"]"#); +} + +#[test] +fn run_custom_command_with_rest_and_arg() { + let actual = nu!( + cwd: ".", + r#" + def rest-me-with-arg [name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-arg "hello" "world" "yay" | to json --raw + "# + ); + + assert_eq!(actual.out, r#"["yay","world","hello"]"#); +} + +#[test] +fn run_custom_command_with_rest_and_flag() { + let actual = nu!( + cwd: ".", + r#" + def rest-me-with-flag [--name: string, ...rest: string] { echo $rest.1 $rest.0 $name}; rest-me-with-flag "hello" "world" --name "yay" | to json --raw + "# + ); + + assert_eq!(actual.out, r#"["world","hello","yay"]"#); +} + +#[test] +fn run_custom_command_with_empty_rest() { + let actual = nu!( + cwd: ".", + r#" + def rest-me-with-empty-rest [...rest: string] { echo $rest }; rest-me-with-empty-rest + "# + ); + + assert_eq!(actual.out, r#""#); + assert_eq!(actual.err, r#""#); +} + +//FIXME: jt: blocked on https://github.com/nushell/engine-q/issues/912 +#[ignore] +#[test] +fn run_custom_command_with_rest_other_name() { + let actual = nu!( + cwd: ".", + r#" + def say-hello [ + greeting:string, + ...names:string # All of the names + ] { + echo $"($greeting), ($names | sort-by | str collect)" + } + say-hello Salutations E D C A B + "# + ); + + assert_eq!(actual.out, r#"Salutations, ABCDE"#); + assert_eq!(actual.err, r#""#); +} + +#[test] +fn alias_a_load_env() { + let actual = nu!( + cwd: ".", + r#" + def activate-helper [] { {BOB: SAM} }; alias activate = load-env (activate-helper); activate; $env.BOB + "# + ); + + assert_eq!(actual.out, r#"SAM"#); +} + +#[test] +fn let_variable() { + let actual = nu!( + cwd: ".", + r#" + let x = 5 + let y = 12 + $x + $y + "# + ); + + assert_eq!(actual.out, "17"); +} + +#[test] +fn let_doesnt_leak() { + let actual = nu!( + cwd: ".", + r#" + do { let x = 5 }; echo $x + "# + ); + + assert!(actual.err.contains("variable not found")); +} + +#[test] +fn let_env_variable() { + let actual = nu!( + cwd: ".", + r#" + let-env TESTENVVAR = "hello world" + echo $env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); +} + +#[test] +fn let_env_hides_variable() { + let actual = nu!( + cwd: ".", + r#" + let-env TESTENVVAR = "hello world" + echo $env.TESTENVVAR + hide TESTENVVAR + echo $env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); + assert!(actual.err.contains("did you mean")); +} + +#[test] +fn let_env_hides_variable_in_parent_scope() { + let actual = nu!( + cwd: ".", + r#" + let-env TESTENVVAR = "hello world" + echo $env.TESTENVVAR + do { + hide TESTENVVAR + echo $env.TESTENVVAR + } + echo $env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); + assert!(actual.err.contains("did you mean")); +} + +#[test] +fn unlet_env_variable() { + let actual = nu!( + cwd: ".", + r#" + let-env TEST_VAR = "hello world" + hide TEST_VAR + echo $env.TEST_VAR + "# + ); + assert!(actual.err.contains("did you mean")); +} + +#[test] +fn unlet_nonexistent_variable() { + let actual = nu!( + cwd: ".", + r#" + hide NONEXISTENT_VARIABLE + "# + ); + + assert!(actual.err.contains("did not find")); +} + +#[test] +fn unlet_variable_in_parent_scope() { + let actual = nu!( + cwd: ".", + r#" + let-env DEBUG = "1" + echo $env.DEBUG + do { + let-env DEBUG = "2" + echo $env.DEBUG + hide DEBUG + echo $env.DEBUG + } + echo $env.DEBUG + "# + ); + + assert_eq!(actual.out, "1211"); +} + +#[test] +fn let_env_doesnt_leak() { + let actual = nu!( + cwd: ".", + r#" + do { let-env xyz = "my message" }; echo $env.xyz + "# + ); + + assert!(actual.err.contains("did you mean")); +} + +#[test] +fn proper_shadow_let_env_aliases() { + let actual = nu!( + cwd: ".", + r#" + let-env DEBUG = true; echo $env.DEBUG | table; do { let-env DEBUG = false; echo $env.DEBUG } | table; echo $env.DEBUG + "# + ); + assert_eq!(actual.out, "truefalsetrue"); +} + +#[test] +fn load_env_variable() { + let actual = nu!( + cwd: ".", + r#" + echo {TESTENVVAR: "hello world"} | load-env + echo $env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); +} + +#[test] +fn load_env_variable_arg() { + let actual = nu!( + cwd: ".", + r#" + load-env {TESTENVVAR: "hello world"} + echo $env.TESTENVVAR + "# + ); + + assert_eq!(actual.out, "hello world"); +} + +#[test] +fn load_env_doesnt_leak() { + let actual = nu!( + cwd: ".", + r#" + do { echo { name: xyz, value: "my message" } | load-env }; echo $env.xyz + "# + ); + + assert!(actual.err.contains("did you mean")); +} + +#[test] +fn proper_shadow_load_env_aliases() { + let actual = nu!( + cwd: ".", + r#" + let-env DEBUG = true; echo $env.DEBUG | table; do { echo {DEBUG: "false"} | load-env; echo $env.DEBUG } | table; echo $env.DEBUG + "# + ); + assert_eq!(actual.out, "truefalsetrue"); +} + +//FIXME: jt: load-env can not currently hide variables because $nothing no longer hides +#[ignore] +#[test] +fn load_env_can_hide_var_envs() { + let actual = nu!( + cwd: ".", + r#" + let-env DEBUG = "1" + echo $env.DEBUG + load-env [[name, value]; [DEBUG $nothing]] + echo $env.DEBUG + "# + ); + assert_eq!(actual.out, "1"); + assert!(actual.err.contains("error")); + assert!(actual.err.contains("Unknown column")); +} + +//FIXME: jt: load-env can not currently hide variables because $nothing no longer hides +#[ignore] +#[test] +fn load_env_can_hide_var_envs_in_parent_scope() { + let actual = nu!( + cwd: ".", + r#" + let-env DEBUG = "1" + echo $env.DEBUG + do { + load-env [[name, value]; [DEBUG $nothing]] + echo $env.DEBUG + } + echo $env.DEBUG + "# + ); + assert_eq!(actual.out, "11"); + assert!(actual.err.contains("error")); + assert!(actual.err.contains("Unknown column")); +} + +#[test] +fn proper_shadow_let_aliases() { + let actual = nu!( + cwd: ".", + r#" + let DEBUG = $false; echo $DEBUG | table; do { let DEBUG = $true; echo $DEBUG } | table; echo $DEBUG + "# + ); + assert_eq!(actual.out, "falsetruefalse"); +} + +#[test] +fn block_params_override() { + let actual = nu!( + cwd: ".", + r#" + [1, 2, 3] | each { |a| echo $it } + "# + ); + assert!(actual.err.contains("variable not found")); +} + +#[test] +fn block_params_override_correct() { + let actual = nu!( + cwd: ".", + r#" + [1, 2, 3] | each { |a| echo $a } | to json --raw + "# + ); + assert_eq!(actual.out, "[1,2,3]"); +} + +#[test] +fn hex_number() { + let actual = nu!( + cwd: ".", + r#" + 0x10 + "# + ); + assert_eq!(actual.out, "16"); +} + +#[test] +fn binary_number() { + let actual = nu!( + cwd: ".", + r#" + 0b10 + "# + ); + assert_eq!(actual.out, "2"); +} + +#[test] +fn octal_number() { + let actual = nu!( + cwd: ".", + r#" + 0o10 + "# + ); + assert_eq!(actual.out, "8"); +} + +#[test] +fn run_dynamic_blocks() { + let actual = nu!( + cwd: ".", + r#" + let block = { echo "holaaaa" }; do $block + "# + ); + assert_eq!(actual.out, "holaaaa"); +} + +#[cfg(feature = "which")] +#[test] +fn argument_subexpression_reports_errors() { + let actual = nu!( + cwd: ".", + "echo (ferris_is_not_here.exe)" + ); + + assert!(!actual.err.is_empty()); +} + +#[test] +fn can_process_one_row_from_internal_and_pipes_it_to_stdin_of_external() { + let actual = nu!( + cwd: ".", + r#"echo "nushelll" | nu --testbin chop"# + ); + + assert_eq!(actual.out, "nushell"); +} + +#[test] +fn bad_operator() { + let actual = nu!( + cwd: ".", + r#" + 2 $ 2 + "# + ); + + assert!(actual.err.contains("operator")); +} + +#[test] +fn index_out_of_bounds() { + let actual = nu!( + cwd: ".", + r#" + let foo = [1, 2, 3]; echo $foo.5 + "# + ); + + assert!(actual.err.contains("too large")); +} + +//FIXME: jt - umm, do we actually want to support this? +#[ignore] +#[test] +fn dash_def() { + let actual = nu!( + cwd: ".", + r#" + def - [x, y] { $x - $y }; - 4 1 + "# + ); + + assert_eq!(actual.out, "3"); +} + +#[test] +fn negative_decimal_start() { + let actual = nu!( + cwd: ".", + r#" + -1.3 + 4 + "# + ); + + assert_eq!(actual.out, "2.7"); +} + +#[test] +fn string_inside_of() { + let actual = nu!( + cwd: ".", + r#" + "bob" in "bobby" + "# + ); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn string_not_inside_of() { + let actual = nu!( + cwd: ".", + r#" + "bob" not-in "bobby" + "# + ); + + assert_eq!(actual.out, "false"); +} + +#[test] +fn index_row() { + let actual = nu!( + cwd: ".", + r#" + let foo = [[name]; [joe] [bob]]; echo $foo.1 | to json --raw + "# + ); + + assert_eq!(actual.out, r#"{"name": "bob"}"#); +} + +#[test] +fn index_cell() { + let actual = nu!( + cwd: ".", + r#" + let foo = [[name]; [joe] [bob]]; echo $foo.name.1 + "# + ); + + assert_eq!(actual.out, "bob"); +} + +#[test] +fn index_cell_alt() { + let actual = nu!( + cwd: ".", + r#" + let foo = [[name]; [joe] [bob]]; echo $foo.1.name + "# + ); + + assert_eq!(actual.out, "bob"); +} + +#[test] +fn not_echoing_ranges_without_numbers() { + let actual = nu!( + cwd: ".", + r#" + echo .. + "# + ); + + assert_eq!(actual.out, ".."); +} + +#[test] +fn not_echoing_exclusive_ranges_without_numbers() { + let actual = nu!( + cwd: ".", + r#" + echo ..< + "# + ); + + assert_eq!(actual.out, "..<"); +} + +#[test] +fn echoing_ranges() { + let actual = nu!( + cwd: ".", + r#" + echo 1..3 | math sum + "# + ); + + assert_eq!(actual.out, "6"); +} + +#[test] +fn echoing_exclusive_ranges() { + let actual = nu!( + cwd: ".", + r#" + echo 1..<4 | math sum + "# + ); + + assert_eq!(actual.out, "6"); +} + +#[test] +fn table_literals1() { + let actual = nu!( + cwd: ".", + r#" + echo [[name age]; [foo 13]] | get age + "# + ); + + assert_eq!(actual.out, "13"); +} + +#[test] +fn table_literals2() { + let actual = nu!( + cwd: ".", + r#" + echo [[name age] ; [bob 13] [sally 20]] | get age | math sum + "# + ); + + assert_eq!(actual.out, "33"); +} + +#[test] +fn list_with_commas() { + let actual = nu!( + cwd: ".", + r#" + echo [1, 2, 3] | math sum + "# + ); + + assert_eq!(actual.out, "6"); +} + +#[test] +fn range_with_left_var() { + let actual = nu!( + cwd: ".", + r#" + ({ size: 3}.size)..10 | math sum + "# + ); + + assert_eq!(actual.out, "52"); +} + +#[test] +fn range_with_right_var() { + let actual = nu!( + cwd: ".", + r#" + 4..({ size: 30}.size) | math sum + "# + ); + + assert_eq!(actual.out, "459"); +} + +#[test] +fn range_with_open_left() { + let actual = nu!( + cwd: ".", + r#" + echo ..30 | math sum + "# + ); + + assert_eq!(actual.out, "465"); +} + +#[test] +fn exclusive_range_with_open_left() { + let actual = nu!( + cwd: ".", + r#" + echo ..<31 | math sum + "# + ); + + assert_eq!(actual.out, "465"); +} + +#[test] +fn range_with_open_right() { + let actual = nu!( + cwd: ".", + r#" + echo 5.. | first 10 | math sum + "# + ); + + assert_eq!(actual.out, "95"); +} + +#[test] +fn exclusive_range_with_open_right() { + let actual = nu!( + cwd: ".", + r#" + echo 5..< | first 10 | math sum + "# + ); + + assert_eq!(actual.out, "95"); +} + +#[test] +fn range_with_mixed_types() { + let actual = nu!( + cwd: ".", + r#" + echo 1..10.5 | math sum + "# + ); + + assert_eq!(actual.out, "55"); +} + +#[test] +fn filesize_math() { + let actual = nu!( + cwd: ".", + r#" + 100 * 10kib + "# + ); + + assert_eq!(actual.out, "1000.0 KiB"); + // why 1000.0 KB instead of 1.0 MB? + // looks like `byte.get_appropriate_unit(false)` behaves this way +} + +#[test] +fn filesize_math2() { + let actual = nu!( + cwd: ".", + r#" + 100 / 10kb + "# + ); + + assert!(actual.err.contains("doesn't support")); +} + +#[test] +fn filesize_math3() { + let actual = nu!( + cwd: ".", + r#" + 100kib / 10 + "# + ); + + assert_eq!(actual.out, "10.0 KiB"); +} +#[test] +fn filesize_math4() { + let actual = nu!( + cwd: ".", + r#" + 100kib * 5 + "# + ); + + assert_eq!(actual.out, "500.0 KiB"); +} + +#[test] +fn filesize_math5() { + let actual = nu!( + cwd: ".", + r#" + 1000 * 1kib + "# + ); + + assert_eq!(actual.out, "1000.0 KiB"); +} + +#[test] +fn filesize_math6() { + let actual = nu!( + cwd: ".", + r#" + 1000 * 1mib + "# + ); + + assert_eq!(actual.out, "1000.0 MiB"); +} + +#[test] +fn filesize_math7() { + let actual = nu!( + cwd: ".", + r#" + 1000 * 1gib + "# + ); + + assert_eq!(actual.out, "1000.0 GiB"); +} + +#[test] +fn exclusive_range_with_mixed_types() { + let actual = nu!( + cwd: ".", + r#" + echo 1..<10.5 | math sum + "# + ); + + assert_eq!(actual.out, "55"); +} + +#[test] +fn table_with_commas() { + let actual = nu!( + cwd: ".", + r#" + echo [[name, age, height]; [JT, 42, 185] [Unknown, 99, 99]] | get age | math sum + "# + ); + + assert_eq!(actual.out, "141"); +} + +#[test] +fn duration_overflow() { + let actual = nu!( + cwd: ".", pipeline( + r#" + ls | get modified | each { $it + 10000000000000000day } + "#) + ); + + assert!(actual.err.contains("duration too large")); +} + +#[test] +fn date_and_duration_overflow() { + let actual = nu!( + cwd: ".", pipeline( + r#" + ls | get modified | each { $it + 1000000000day } + "#) + ); + + // assert_eq!(actual.err, "overflow"); + assert!(actual.err.contains("duration too large")); +} + +#[test] +fn pipeline_params_simple() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1 2 3 | $in.1 * $in.2 + "#) + ); + + assert_eq!(actual.out, "6"); +} + +#[test] +fn pipeline_params_inner() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1 2 3 | (echo $in.2 6 7 | $in.0 * $in.1 * $in.2) + "#) + ); + + assert_eq!(actual.out, "126"); +} + +#[test] +fn better_table_lex() { + let actual = nu!( + cwd: ".", pipeline( + r#" + let table = [ + [name, size]; + [small, 7] + [medium, 10] + [large, 12] + ]; + $table.1.size + "#) + ); + + assert_eq!(actual.out, "10"); +} + +#[test] +fn better_subexpr_lex() { + let actual = nu!( + cwd: ".", pipeline( + r#" + (echo boo + sam | str length | math sum) + "#) + ); + + assert_eq!(actual.out, "6"); +} + +#[test] +fn subsubcommand() { + let actual = nu!( + cwd: ".", pipeline( + r#" + def "aws s3 rb" [url] { $url + " loaded" }; aws s3 rb localhost + "#) + ); + + assert_eq!(actual.out, "localhost loaded"); +} + +#[test] +fn manysubcommand() { + let actual = nu!( + cwd: ".", pipeline( + r#" + def "aws s3 rb ax vf qqqq rrrr" [url] { $url + " loaded" }; aws s3 rb ax vf qqqq rrrr localhost + "#) + ); + + assert_eq!(actual.out, "localhost loaded"); +} + +#[test] +fn nothing_string_1() { + let actual = nu!( + cwd: ".", pipeline( + r#" + $nothing == "foo" + "#) + ); + + assert_eq!(actual.out, "false"); +} + +// FIXME: no current way to hide aliases +#[ignore] +#[test] +fn unalias_shadowing() { + let actual = nu!( + cwd: ".", pipeline( + r#" + def test-shadowing [] { + alias greet = echo hello; + let xyz = { greet }; + unalias greet; + do $xyz + }; + test-shadowing + "#) + ); + assert_eq!(actual.out, "hello"); +} + +// FIXME: no current way to hide aliases +#[ignore] +#[test] +fn unalias_does_not_escape_scope() { + let actual = nu!( + cwd: ".", pipeline( + r#" + def test-alias [] { + alias greet = echo hello; + (unalias greet); + greet + }; + test-alias + "#) + ); + assert_eq!(actual.out, "hello"); +} + +// FIXME: no current way to hide aliases +#[ignore] +#[test] +fn unalias_hides_alias() { + let actual = nu!(cwd: ".", pipeline( + r#" + def test-alias [] { + alias ll = ls -l; + unalias ll; + ll + }; + test-alias + "#) + ); + + assert!(actual.err.contains("not found")); +} + +mod parse { + use nu_test_support::nu; + + /* + The debug command's signature is: + + Usage: + > debug {flags} + + flags: + -h, --help: Display this help message + -r, --raw: Prints the raw value representation. + */ + + #[test] + fn errors_if_flag_passed_is_not_exact() { + let actual = nu!(cwd: ".", "debug -ra"); + + assert!(actual.err.contains("unknown flag"),); + + let actual = nu!(cwd: ".", "debug --rawx"); + + assert!(actual.err.contains("unknown flag"),); + } + + #[test] + fn errors_if_flag_is_not_supported() { + let actual = nu!(cwd: ".", "debug --ferris"); + + assert!(actual.err.contains("unknown flag"),); + } + + #[test] + fn errors_if_passed_an_unexpected_argument() { + let actual = nu!(cwd: ".", "debug ferris"); + + assert!(actual.err.contains("extra positional argument"),); + } +} + +mod tilde_expansion { + use nu_test_support::nu; + + #[test] + #[should_panic] + fn as_home_directory_when_passed_as_argument_and_begins_with_tilde() { + let actual = nu!( + cwd: ".", + r#" + echo ~ + "# + ); + + assert!(!actual.out.contains('~'),); + } + + #[test] + fn does_not_expand_when_passed_as_argument_and_does_not_start_with_tilde() { + let actual = nu!( + cwd: ".", + r#" + echo "1~1" + "# + ); + + assert_eq!(actual.out, "1~1"); + } +} + +mod variable_scoping { + use nu_test_support::nu; + + macro_rules! test_variable_scope { + ($func:literal == $res:literal $(,)*) => { + let actual = nu!( + cwd: ".", + $func + ); + + assert_eq!(actual.out, $res); + }; + } + macro_rules! test_variable_scope_list { + ($func:literal == $res:expr $(,)*) => { + let actual = nu!( + cwd: ".", + $func + ); + + let result: Vec<&str> = actual.out.matches("ZZZ").collect(); + assert_eq!(result, $res); + }; + } + + #[test] + fn access_variables_in_scopes() { + test_variable_scope!( + r#" def test [input] { echo [0 1 2] | do { do { echo $input } } } + test ZZZ "# + == "ZZZ" + ); + test_variable_scope!( + r#" def test [input] { echo [0 1 2] | do { do { if $input == "ZZZ" { echo $input } else { echo $input } } } } + test ZZZ "# + == "ZZZ" + ); + test_variable_scope!( + r#" def test [input] { echo [0 1 2] | do { do { if $input == "ZZZ" { echo $input } else { echo $input } } } } + test ZZZ "# + == "ZZZ" + ); + test_variable_scope!( + r#" def test [input] { echo [0 1 2] | do { echo $input } } + test ZZZ "# + == "ZZZ" + ); + test_variable_scope!( + r#" def test [input] { echo [0 1 2] | do { if $input == $input { echo $input } else { echo $input } } } + test ZZZ "# + == "ZZZ" + ); + test_variable_scope_list!( + r#" def test [input] { echo [0 1 2] | each { echo $input } } + test ZZZ "# + == ["ZZZ", "ZZZ", "ZZZ"] + ); + test_variable_scope_list!( + r#" def test [input] { echo [0 1 2] | each { if $it > 0 {echo $input} else {echo $input}} } + test ZZZ "# + == ["ZZZ", "ZZZ", "ZZZ"] + ); + test_variable_scope_list!( + r#" def test [input] { echo [0 1 2] | each { if $input == $input {echo $input} else {echo $input}} } + test ZZZ "# + == ["ZZZ", "ZZZ", "ZZZ"] + ); + } +} diff --git a/tests/shell/pipeline/commands/mod.rs b/tests/shell/pipeline/commands/mod.rs new file mode 100644 index 0000000000..5b7aa7e195 --- /dev/null +++ b/tests/shell/pipeline/commands/mod.rs @@ -0,0 +1,2 @@ +mod external; +mod internal; diff --git a/tests/shell/pipeline/mod.rs b/tests/shell/pipeline/mod.rs new file mode 100644 index 0000000000..e8fb3af058 --- /dev/null +++ b/tests/shell/pipeline/mod.rs @@ -0,0 +1,10 @@ +mod commands; + +use nu_test_support::nu; + +#[test] +fn doesnt_break_on_utf8() { + let actual = nu!(cwd: ".", "echo ö"); + + assert_eq!(actual.out, "ö", "'{}' should contain ö", actual.out); +}