diff --git a/crates/nu-command/src/core_commands/tutor.rs b/crates/nu-command/src/core_commands/tutor.rs index 6d28767b71..818ccb89db 100644 --- a/crates/nu-command/src/core_commands/tutor.rs +++ b/crates/nu-command/src/core_commands/tutor.rs @@ -426,7 +426,7 @@ fn display(help: &str, engine_state: &EngineState, stack: &mut Stack, span: Span if let Ok(output) = decl.run( engine_state, stack, - &Call::new(), + &Call::new(span), Value::String { val: item.to_string(), span: Span { start: 0, end: 0 }, diff --git a/crates/nu-command/src/filesystem/open.rs b/crates/nu-command/src/filesystem/open.rs index 5d53e2d612..d3afce0d34 100644 --- a/crates/nu-command/src/filesystem/open.rs +++ b/crates/nu-command/src/filesystem/open.rs @@ -142,7 +142,7 @@ impl Command for Open { Some(converter_id) => engine_state.get_decl(converter_id).run( engine_state, stack, - &Call::new(), + &Call::new(call_span), output, ), None => Ok(output), diff --git a/crates/nu-command/src/filesystem/save.rs b/crates/nu-command/src/filesystem/save.rs index 5b2efcd781..fa660f04c7 100644 --- a/crates/nu-command/src/filesystem/save.rs +++ b/crates/nu-command/src/filesystem/save.rs @@ -71,7 +71,7 @@ impl Command for Save { let output = engine_state.get_decl(converter_id).run( engine_state, stack, - &Call::new(), + &Call::new(span), input, )?; diff --git a/crates/nu-command/src/filters/columns.rs b/crates/nu-command/src/filters/columns.rs index 38e2ac22cb..303251a0f5 100644 --- a/crates/nu-command/src/filters/columns.rs +++ b/crates/nu-command/src/filters/columns.rs @@ -82,6 +82,10 @@ fn getcol( .map(move |x| Value::String { val: x, span }) .into_pipeline_data(engine_state.ctrlc.clone())) } + PipelineData::Value(Value::Record { cols, .. }, ..) => Ok(cols + .into_iter() + .map(move |x| Value::String { val: x, span }) + .into_pipeline_data(engine_state.ctrlc.clone())), PipelineData::Value(..) | PipelineData::RawStream(..) => { let cols = vec![]; let vals = vec![]; diff --git a/crates/nu-command/src/filters/lines.rs b/crates/nu-command/src/filters/lines.rs index a756282bd8..c5c13647e2 100644 --- a/crates/nu-command/src/filters/lines.rs +++ b/crates/nu-command/src/filters/lines.rs @@ -30,7 +30,7 @@ impl Command for Lines { input: PipelineData, ) -> Result { let head = call.head; - let skip_empty = call.has_flag("skip-emtpy"); + let skip_empty = call.has_flag("skip-empty"); match input { #[allow(clippy::needless_collect)] // Collect is needed because the string may not live long enough for @@ -39,13 +39,21 @@ impl Command for Lines { PipelineData::Value(Value::String { val, span }, ..) => { let split_char = if val.contains("\r\n") { "\r\n" } else { "\n" }; - let lines = val + let mut lines = val .split(split_char) .map(|s| s.to_string()) .collect::>(); + // if the last one is empty, remove it, as it was just + // a newline at the end of the input we got + if let Some(last) = lines.last() { + if last.is_empty() { + lines.pop(); + } + } + let iter = lines.into_iter().filter_map(move |s| { - if skip_empty && s.is_empty() { + if skip_empty && s.trim().is_empty() { None } else { Some(Value::string(s, span)) @@ -65,21 +73,30 @@ impl Command for Lines { split_char = "\r\n"; } - let inner = val + let mut lines = val .split(split_char) .filter_map(|s| { - if skip_empty && s.is_empty() { + if skip_empty && s.trim().is_empty() { None } else { - Some(Value::String { - val: s.into(), - span, - }) + Some(s.to_string()) } }) - .collect::>(); + .collect::>(); - Some(inner) + // if the last one is empty, remove it, as it was just + // a newline at the end of the input we got + if let Some(last) = lines.last() { + if last.is_empty() { + lines.pop(); + } + } + + Some( + lines + .into_iter() + .map(move |x| Value::String { val: x, span }), + ) } else { None } @@ -102,13 +119,21 @@ impl Command for Lines { let split_char = if s.contains("\r\n") { "\r\n" } else { "\n" }; #[allow(clippy::needless_collect)] - let lines = s + let mut lines = s .split(split_char) .map(|s| s.to_string()) .collect::>(); + // if the last one is empty, remove it, as it was just + // a newline at the end of the input we got + if let Some(last) = lines.last() { + if last.is_empty() { + lines.pop(); + } + } + let iter = lines.into_iter().filter_map(move |s| { - if skip_empty && s.is_empty() { + if skip_empty && s.trim().is_empty() { None } else { Some(Value::string(s, head)) diff --git a/crates/nu-command/src/formats/from/ics.rs b/crates/nu-command/src/formats/from/ics.rs index aacb77e9bd..c320de7bd5 100644 --- a/crates/nu-command/src/formats/from/ics.rs +++ b/crates/nu-command/src/formats/from/ics.rs @@ -94,6 +94,13 @@ END:VCALENDAR' | from ics", fn from_ics(input: PipelineData, head: Span, config: &Config) -> Result { let input_string = input.collect_string("", config)?; + + let input_string = input_string + .lines() + .map(|x| x.trim().to_string()) + .collect::>() + .join("\n"); + let input_bytes = input_string.as_bytes(); let buf_reader = BufReader::new(input_bytes); let parser = ical::IcalParser::new(buf_reader); @@ -103,9 +110,9 @@ fn from_ics(input: PipelineData, head: Span, config: &Config) -> Result output.push(calendar_to_value(c, head)), - Err(_) => output.push(Value::Error { + Err(e) => output.push(Value::Error { error: ShellError::UnsupportedInput( - "input cannot be parsed as .ics".to_string(), + format!("input cannot be parsed as .ics ({})", e), head, ), }), diff --git a/crates/nu-command/src/formats/from/vcf.rs b/crates/nu-command/src/formats/from/vcf.rs index fbd1d648d6..4b98535474 100644 --- a/crates/nu-command/src/formats/from/vcf.rs +++ b/crates/nu-command/src/formats/from/vcf.rs @@ -125,14 +125,24 @@ END:VCARD' | from vcf", fn from_vcf(input: PipelineData, head: Span, config: &Config) -> Result { let input_string = input.collect_string("", config)?; + + let input_string = input_string + .lines() + .map(|x| x.trim().to_string()) + .collect::>() + .join("\n"); + let input_bytes = input_string.as_bytes(); let cursor = std::io::Cursor::new(input_bytes); let parser = ical::VcardParser::new(cursor); let iter = parser.map(move |contact| match contact { Ok(c) => contact_to_value(c, head), - Err(_) => Value::Error { - error: ShellError::UnsupportedInput("input cannot be parsed as .vcf".to_string(), head), + Err(e) => Value::Error { + error: ShellError::UnsupportedInput( + format!("input cannot be parsed as .vcf ({})", e), + head, + ), }, }); diff --git a/crates/nu-command/src/network/fetch.rs b/crates/nu-command/src/network/fetch.rs index 993077a56b..84a72614a9 100644 --- a/crates/nu-command/src/network/fetch.rs +++ b/crates/nu-command/src/network/fetch.rs @@ -265,7 +265,7 @@ fn helper( Some(converter_id) => engine_state.get_decl(converter_id).run( engine_state, stack, - &Call::new(), + &Call::new(span), output, ), None => Ok(output), diff --git a/crates/nu-command/src/strings/str_/trim/command.rs b/crates/nu-command/src/strings/str_/trim/command.rs index 2063ec0ccc..6a1d7e0928 100644 --- a/crates/nu-command/src/strings/str_/trim/command.rs +++ b/crates/nu-command/src/strings/str_/trim/command.rs @@ -2,14 +2,14 @@ use nu_engine::CallExt; use nu_protocol::{ ast::{Call, CellPath}, engine::{Command, EngineState, Stack}, - Example, PipelineData, ShellError, Signature, Span, SyntaxShape, Value, + Example, PipelineData, ShellError, Signature, Span, Spanned, SyntaxShape, Value, }; #[derive(Clone)] pub struct SubCommand; struct Arguments { - character: Option, + character: Option>, column_paths: Vec, } @@ -143,7 +143,16 @@ where input, ); let to_trim = match options.character.as_ref() { - Some(v) => v.as_string()?.chars().next(), + Some(v) => { + if v.item.chars().count() > 1 { + return Err(ShellError::SpannedLabeledError( + "Trim only works with single character".into(), + "needs single character".into(), + v.span, + )); + } + v.item.chars().next() + } None => None, }; diff --git a/crates/nu-command/src/system/run_external.rs b/crates/nu-command/src/system/run_external.rs index 560816cc9f..4731789c42 100644 --- a/crates/nu-command/src/system/run_external.rs +++ b/crates/nu-command/src/system/run_external.rs @@ -220,7 +220,7 @@ impl ExternalCommand { &crate::Table, &engine_state, &mut stack, - &Call::new(), + &Call::new(head), input, ); diff --git a/crates/nu-command/tests/commands/all.rs b/crates/nu-command/tests/commands/all.rs new file mode 100644 index 0000000000..25a263f6a1 --- /dev/null +++ b/crates/nu-command/tests/commands/all.rs @@ -0,0 +1,57 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn checks_all_rows_are_true() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ "Andrés", "Andrés", "Andrés" ] + | all? $it == "Andrés" + "# + )); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn checks_all_rows_are_false_with_param() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3, 4] | all? { |a| $a >= 5 } + "# + )); + + assert_eq!(actual.out, "false"); +} + +#[test] +fn checks_all_rows_are_true_with_param() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1, 2, 3, 4] | all? { |a| $a < 5 } + "# + )); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn checks_all_columns_of_a_table_is_true() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ + [ first_name, last_name, rusty_at, likes ]; + [ Andrés, Robalino, 10/11/2013, 1 ] + [ Jonathan, Turner, 10/12/2013, 1 ] + [ Darren, Schroeder, 10/11/2013, 1 ] + [ Yehuda, Katz, 10/11/2013, 1 ] + ] + | all? likes > 0 + "# + )); + + assert_eq!(actual.out, "true"); +} diff --git a/crates/nu-command/tests/commands/any.rs b/crates/nu-command/tests/commands/any.rs new file mode 100644 index 0000000000..817c6605ab --- /dev/null +++ b/crates/nu-command/tests/commands/any.rs @@ -0,0 +1,33 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn checks_any_row_is_true() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ "Ecuador", "USA", "New Zealand" ] + | any? $it == "New Zealand" + "# + )); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn checks_any_column_of_a_table_is_true() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ + [ first_name, last_name, rusty_at, likes ]; + [ Andrés, Robalino, 10/11/2013, 1 ] + [ Jonathan, Turner, 10/12/2013, 1 ] + [ Darren, Schroeder, 10/11/2013, 1 ] + [ Yehuda, Katz, 10/11/2013, 1 ] + ] + | any? rusty_at == 10/12/2013 + "# + )); + + assert_eq!(actual.out, "true"); +} diff --git a/crates/nu-command/tests/commands/append.rs b/crates/nu-command/tests/commands/append.rs new file mode 100644 index 0000000000..9652348e51 --- /dev/null +++ b/crates/nu-command/tests/commands/append.rs @@ -0,0 +1,15 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn adds_a_row_to_the_end() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ "Andrés N. Robalino", "Jonathan Turner", "Yehuda Katz" ] + | append "pollo loco" + | nth 3 + "# + )); + + assert_eq!(actual.out, "pollo loco"); +} diff --git a/crates/nu-command/tests/commands/cal.rs b/crates/nu-command/tests/commands/cal.rs new file mode 100644 index 0000000000..91024408f7 --- /dev/null +++ b/crates/nu-command/tests/commands/cal.rs @@ -0,0 +1,87 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn cal_full_year() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal -y --full-year 2010 | first | to json + "# + )); + + let first_week_2010_json = r#"{"year":2010,"sunday":null,"monday":null,"tuesday":null,"wednesday":null,"thursday":null,"friday":1,"saturday":2}"#; + + assert_eq!(actual.out, first_week_2010_json); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn cal_february_2020_leap_year() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal -ym --full-year 2020 --month-names | where month == "february" | to json + "# + )); + + let cal_february_json = r#"[{"year":2020,"month":"february","sunday":null,"monday":null,"tuesday":null,"wednesday":null,"thursday":null,"friday":null,"saturday":1},{"year":2020,"month":"february","sunday":2,"monday":3,"tuesday":4,"wednesday":5,"thursday":6,"friday":7,"saturday":8},{"year":2020,"month":"february","sunday":9,"monday":10,"tuesday":11,"wednesday":12,"thursday":13,"friday":14,"saturday":15},{"year":2020,"month":"february","sunday":16,"monday":17,"tuesday":18,"wednesday":19,"thursday":20,"friday":21,"saturday":22},{"year":2020,"month":"february","sunday":23,"monday":24,"tuesday":25,"wednesday":26,"thursday":27,"friday":28,"saturday":29}]"#; + + assert_eq!(actual.out, cal_february_json); +} + +#[test] +fn cal_friday_the_thirteenths_in_2015() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal --full-year 2015 | default friday 0 | where friday == 13 | length + "# + )); + + assert!(actual.out.contains('3')); +} + +#[test] +fn cal_rows_in_2020() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal --full-year 2020 | length + "# + )); + + assert!(actual.out.contains("62")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn cal_week_day_start_monday() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal --full-year 2020 -m --month-names --week-start monday | where month == january | to json + "# + )); + + let cal_january_json = r#"[{"month":"january","monday":null,"tuesday":null,"wednesday":1,"thursday":2,"friday":3,"saturday":4,"sunday":5},{"month":"january","monday":6,"tuesday":7,"wednesday":8,"thursday":9,"friday":10,"saturday":11,"sunday":12},{"month":"january","monday":13,"tuesday":14,"wednesday":15,"thursday":16,"friday":17,"saturday":18,"sunday":19},{"month":"january","monday":20,"tuesday":21,"wednesday":22,"thursday":23,"friday":24,"saturday":25,"sunday":26},{"month":"january","monday":27,"tuesday":28,"wednesday":29,"thursday":30,"friday":31,"saturday":null,"sunday":null}]"#; + + assert_eq!(actual.out, cal_january_json); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn cal_sees_pipeline_year() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal --full-year 1020 | get monday | first 3 | to json + "# + )); + + assert_eq!(actual.out, "[3,10,17]"); +} diff --git a/crates/nu-command/tests/commands/cd.rs b/crates/nu-command/tests/commands/cd.rs new file mode 100644 index 0000000000..58b468e325 --- /dev/null +++ b/crates/nu-command/tests/commands/cd.rs @@ -0,0 +1,277 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::nu; +use nu_test_support::playground::Playground; +use std::path::PathBuf; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_from_current_directory_using_relative_path() { + Playground::setup("cd_test_1", |dirs, _| { + let actual = nu!( + cwd: dirs.root(), + r#" + cd cd_test_1 + echo (pwd) + "# + ); + + assert_eq!(PathBuf::from(actual.out), *dirs.test()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_from_current_directory_using_absolute_path() { + Playground::setup("cd_test_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + r#" + cd "{}" + echo (pwd) + "#, + dirs.formats() + ); + + assert_eq!(PathBuf::from(actual.out), dirs.formats()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_switch_back_to_previous_working_directory() { + Playground::setup("cd_test_3", |dirs, sandbox| { + sandbox.mkdir("odin"); + + let actual = nu!( + cwd: dirs.test().join("odin"), + r#" + cd {} + cd - + echo (pwd) + "#, + dirs.test() + ); + + assert_eq!(PathBuf::from(actual.out), dirs.test().join("odin")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesytem_change_from_current_directory_using_relative_path_and_dash() { + Playground::setup("cd_test_4", |dirs, sandbox| { + sandbox.within("odin").mkdir("-"); + + let actual = nu!( + cwd: dirs.test(), + r#" + cd odin/- + echo (pwd) + "# + ); + + assert_eq!( + PathBuf::from(actual.out), + dirs.test().join("odin").join("-") + ); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_current_directory_to_parent_directory() { + Playground::setup("cd_test_5", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + r#" + cd .. + echo (pwd) + "# + ); + + assert_eq!(PathBuf::from(actual.out), *dirs.root()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_current_directory_to_two_parents_up_using_multiple_dots() { + Playground::setup("cd_test_6", |dirs, sandbox| { + sandbox.within("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test().join("foo/bar"), + r#" + cd ... + echo (pwd) + "# + ); + + assert_eq!(PathBuf::from(actual.out), *dirs.test()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_current_directory_to_parent_directory_after_delete_cwd() { + Playground::setup("cd_test_7", |dirs, sandbox| { + sandbox.within("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test().join("foo/bar"), + r#" + rm {}/foo/bar + echo "," + cd .. + echo (pwd) + "#, + dirs.test() + ); + + let actual = actual.out.split(',').nth(1).unwrap(); + + assert_eq!(PathBuf::from(actual), *dirs.test().join("foo")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_to_home_directory() { + Playground::setup("cd_test_8", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + r#" + cd ~ + echo (pwd) + "# + ); + + assert_eq!(Some(PathBuf::from(actual.out)), dirs_next::home_dir()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_to_a_directory_containing_spaces() { + Playground::setup("cd_test_9", |dirs, sandbox| { + sandbox.mkdir("robalino turner katz"); + + let actual = nu!( + cwd: dirs.test(), + r#" + cd "robalino turner katz" + echo (pwd) + "# + ); + + assert_eq!( + PathBuf::from(actual.out), + dirs.test().join("robalino turner katz") + ); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_not_a_directory() { + Playground::setup("cd_test_10", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("ferris_did_it.txt")]); + + let actual = nu!( + cwd: dirs.test(), + "cd ferris_did_it.txt" + ); + + assert!( + actual.err.contains("ferris_did_it.txt"), + "actual={:?}", + actual.err + ); + assert!( + actual.err.contains("is not a directory"), + "actual={:?}", + actual.err + ); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_directory_not_found() { + Playground::setup("cd_test_11", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + "cd dir_that_does_not_exist" + + ); + + assert!( + actual.err.contains("dir_that_does_not_exist"), + "actual={:?}", + actual.err + ); + assert!( + actual.err.contains("directory not found"), + "actual={:?}", + actual.err + ); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn filesystem_change_directory_to_symlink_relative() { + Playground::setup("cd_test_12", |dirs, sandbox| { + sandbox.mkdir("foo"); + sandbox.mkdir("boo"); + sandbox.symlink("foo", "foo_link"); + + let actual = nu!( + cwd: dirs.test().join("boo"), + r#" + cd ../foo_link + echo (pwd) + "# + ); + + assert_eq!(PathBuf::from(actual.out), dirs.test().join("foo")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[cfg(target_os = "windows")] +#[test] +fn test_change_windows_drive() { + Playground::setup("cd_test_20", |dirs, sandbox| { + sandbox.mkdir("test_folder"); + + let _actual = nu!( + cwd: dirs.test(), + r#" + subst Z: test_folder + Z: + echo "some text" | save test_file.txt + cd ~ + subst Z: /d + "# + ); + assert!(dirs + .test() + .join("test_folder") + .join("test_file.txt") + .exists()); + }) +} diff --git a/crates/nu-command/tests/commands/compact.rs b/crates/nu-command/tests/commands/compact.rs new file mode 100644 index 0000000000..21b4db674d --- /dev/null +++ b/crates/nu-command/tests/commands/compact.rs @@ -0,0 +1,50 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn discards_rows_where_given_column_is_empty() { + Playground::setup("compact_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_amigos.json", + r#" + { + "amigos": [ + {"name": "Yehuda", "rusty_luck": 1}, + {"name": "Jonathan", "rusty_luck": 1}, + {"name": "Andres", "rusty_luck": 1}, + {"name":"GorbyPuff"} + ] + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_amigos.json + | get amigos + | compact rusty_luck + | length + "# + )); + + assert_eq!(actual.out, "3"); + }); +} +#[test] +fn discards_empty_rows_by_default() { + Playground::setup("compact_test_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "[1,2,3,14,null]" + | from json + | compact + | length + "# + )); + + assert_eq!(actual.out, "4"); + }); +} diff --git a/crates/nu-command/tests/commands/cp.rs b/crates/nu-command/tests/commands/cp.rs new file mode 100644 index 0000000000..76da707531 --- /dev/null +++ b/crates/nu-command/tests/commands/cp.rs @@ -0,0 +1,241 @@ +use nu_test_support::fs::{files_exist_at, AbsoluteFile, Stub::EmptyFile}; +use nu_test_support::nu; +use nu_test_support::playground::Playground; +use std::path::Path; + +#[test] +fn copies_a_file() { + Playground::setup("cp_test_1", |dirs, _| { + nu!( + cwd: dirs.root(), + "cp \"{}\" cp_test_1/sample.ini", + dirs.formats().join("sample.ini") + ); + + assert!(dirs.test().join("sample.ini").exists()); + }); +} + +#[test] +fn copies_the_file_inside_directory_if_path_to_copy_is_directory() { + Playground::setup("cp_test_2", |dirs, _| { + let expected_file = AbsoluteFile::new(dirs.test().join("sample.ini")); + + nu!( + cwd: dirs.formats(), + "cp ../formats/sample.ini {}", + expected_file.dir() + ); + + assert!(dirs.test().join("sample.ini").exists()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn error_if_attempting_to_copy_a_directory_to_another_directory() { + Playground::setup("cp_test_3", |dirs, _| { + let actual = nu!( + cwd: dirs.formats(), + "cp ../formats {}", dirs.test() + ); + + assert!(actual.err.contains("../formats")); + assert!(actual.err.contains("resolves to a directory (not copied)")); + }); +} + +#[test] +fn copies_the_directory_inside_directory_if_path_to_copy_is_directory_and_with_recursive_flag() { + Playground::setup("cp_test_4", |dirs, sandbox| { + sandbox + .within("originals") + .with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]) + .mkdir("expected"); + + let expected_dir = dirs.test().join("expected").join("originals"); + + nu!( + cwd: dirs.test(), + "cp originals expected -r" + ); + + assert!(expected_dir.exists()); + assert!(files_exist_at( + vec![ + Path::new("yehuda.txt"), + Path::new("jonathan.txt"), + Path::new("andres.txt") + ], + expected_dir + )); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn deep_copies_with_recursive_flag() { + Playground::setup("cp_test_5", |dirs, sandbox| { + sandbox + .within("originals") + .with_files(vec![EmptyFile("manifest.txt")]) + .within("originals/contributors") + .with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]) + .within("originals/contributors/jonathan") + .with_files(vec![EmptyFile("errors.txt"), EmptyFile("multishells.txt")]) + .within("originals/contributors/andres") + .with_files(vec![EmptyFile("coverage.txt"), EmptyFile("commands.txt")]) + .within("originals/contributors/yehuda") + .with_files(vec![EmptyFile("defer-evaluation.txt")]) + .mkdir("expected"); + + let expected_dir = dirs.test().join("expected").join("originals"); + + let jonathans_expected_copied_dir = expected_dir.join("contributors").join("jonathan"); + let andres_expected_copied_dir = expected_dir.join("contributors").join("andres"); + let yehudas_expected_copied_dir = expected_dir.join("contributors").join("yehuda"); + + nu!( + cwd: dirs.test(), + "cp originals expected --recursive" + ); + + assert!(expected_dir.exists()); + assert!(files_exist_at( + vec![Path::new("errors.txt"), Path::new("multishells.txt")], + jonathans_expected_copied_dir + )); + assert!(files_exist_at( + vec![Path::new("coverage.txt"), Path::new("commands.txt")], + andres_expected_copied_dir + )); + assert!(files_exist_at( + vec![Path::new("defer-evaluation.txt")], + yehudas_expected_copied_dir + )); + }) +} + +#[test] +fn copies_using_path_with_wildcard() { + Playground::setup("cp_test_6", |dirs, _| { + nu!( + cwd: dirs.formats(), + "cp ../formats/* {}", dirs.test() + ); + + assert!(files_exist_at( + vec![ + Path::new("caco3_plastics.csv"), + Path::new("cargo_sample.toml"), + Path::new("jonathan.xml"), + Path::new("sample.ini"), + Path::new("sgml_description.json"), + Path::new("utf16.ini"), + ], + dirs.test() + )); + }) +} + +#[test] +fn copies_using_a_glob() { + Playground::setup("cp_test_7", |dirs, _| { + nu!( + cwd: dirs.formats(), + "cp * {}", dirs.test() + ); + + assert!(files_exist_at( + vec![ + Path::new("caco3_plastics.csv"), + Path::new("cargo_sample.toml"), + Path::new("jonathan.xml"), + Path::new("sample.ini"), + Path::new("sgml_description.json"), + Path::new("utf16.ini"), + ], + dirs.test() + )); + }); +} + +#[test] +fn copies_same_file_twice() { + Playground::setup("cp_test_8", |dirs, _| { + nu!( + cwd: dirs.root(), + "cp \"{}\" cp_test_8/sample.ini", + dirs.formats().join("sample.ini") + ); + + nu!( + cwd: dirs.root(), + "cp \"{}\" cp_test_8/sample.ini", + dirs.formats().join("sample.ini") + ); + + assert!(dirs.test().join("sample.ini").exists()); + }); +} + +#[test] +fn copy_files_using_glob_two_parents_up_using_multiple_dots() { + Playground::setup("cp_test_9", |dirs, sandbox| { + sandbox.within("foo").within("bar").with_files(vec![ + EmptyFile("jonathan.json"), + EmptyFile("andres.xml"), + EmptyFile("yehuda.yaml"), + EmptyFile("kevin.txt"), + EmptyFile("many_more.ppl"), + ]); + + nu!( + cwd: dirs.test().join("foo/bar"), + r#" + cp * ... + "# + ); + + assert!(files_exist_at( + vec![ + "yehuda.yaml", + "jonathan.json", + "andres.xml", + "kevin.txt", + "many_more.ppl", + ], + dirs.test() + )); + }) +} + +#[test] +fn copy_file_and_dir_from_two_parents_up_using_multiple_dots_to_current_dir_recursive() { + Playground::setup("cp_test_10", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("hello_there")]); + sandbox.mkdir("hello_again"); + sandbox.within("foo").mkdir("bar"); + + nu!( + cwd: dirs.test().join("foo/bar"), + r#" + cp -r .../hello* . + "# + ); + + let expected = dirs.test().join("foo/bar"); + + assert!(files_exist_at(vec!["hello_there", "hello_again"], expected)); + }) +} diff --git a/crates/nu-command/tests/commands/def.rs b/crates/nu-command/tests/commands/def.rs new file mode 100644 index 0000000000..899b25a20c --- /dev/null +++ b/crates/nu-command/tests/commands/def.rs @@ -0,0 +1,22 @@ +use nu_test_support::nu; +use nu_test_support::playground::Playground; +use std::fs; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn def_with_comment() { + Playground::setup("def_with_comment", |dirs, _| { + let data = r#" +#My echo +def e [arg] {echo $arg} + "#; + fs::write(dirs.root().join("def_test"), data).expect("Unable to write file"); + let actual = nu!( + cwd: dirs.root(), + "source def_test; help e | to json" + ); + + assert!(actual.out.contains("My echo\\n\\n")); + }); +} diff --git a/crates/nu-command/tests/commands/default.rs b/crates/nu-command/tests/commands/default.rs new file mode 100644 index 0000000000..334b45ee68 --- /dev/null +++ b/crates/nu-command/tests/commands/default.rs @@ -0,0 +1,35 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn adds_row_data_if_column_missing() { + Playground::setup("default_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_amigos.json", + r#" + { + "amigos": [ + {"name": "Yehuda"}, + {"name": "Jonathan", "rusty_luck": 0}, + {"name": "Andres", "rusty_luck": 0}, + {"name":"GorbyPuff"} + ] + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_amigos.json + | get amigos + | default rusty_luck 1 + | where rusty_luck == 1 + | length + "# + )); + + assert_eq!(actual.out, "2"); + }); +} diff --git a/crates/nu-command/tests/commands/drop.rs b/crates/nu-command/tests/commands/drop.rs new file mode 100644 index 0000000000..3a2b9cdf41 --- /dev/null +++ b/crates/nu-command/tests/commands/drop.rs @@ -0,0 +1,72 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn columns() { + let actual = nu!( + cwd: ".", pipeline(r#" + echo [ + [arepas, color]; + + [3, white] + [8, yellow] + [4, white] + ] + | drop column + | get + | length + "#) + ); + + assert_eq!(actual.out, "1"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn more_columns_than_table_has() { + let actual = nu!( + cwd: ".", pipeline(r#" + echo [ + [arepas, color]; + + [3, white] + [8, yellow] + [4, white] + ] + | drop column 3 + | get + | empty? + "#) + ); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn rows() { + let actual = nu!( + cwd: ".", pipeline(r#" + echo [ + [arepas]; + + [3] + [8] + [4] + ] + | drop 2 + | get arepas + | math sum + "#) + ); + + assert_eq!(actual.out, "3"); +} + +#[test] +fn more_rows_than_table_has() { + let actual = nu!(cwd: ".", "date | drop 50 | length"); + + assert_eq!(actual.out, "0"); +} diff --git a/crates/nu-command/tests/commands/each.rs b/crates/nu-command/tests/commands/each.rs new file mode 100644 index 0000000000..f8c89ebf66 --- /dev/null +++ b/crates/nu-command/tests/commands/each.rs @@ -0,0 +1,83 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn each_works_separately() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3] | each { echo $it 10 | math sum } | to json + "# + )); + + assert_eq!(actual.out, "[11,12,13]"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn each_group_works() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3 4 5 6] | each group 3 { $it } | to json + "# + )); + + assert_eq!(actual.out, "[[1,2,3],[4,5,6]]"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn each_window() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3 4] | each window 3 { $it } | to json + "# + )); + + assert_eq!(actual.out, "[[1,2,3],[2,3,4]]"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn each_window_stride() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3 4 5 6] | each window 3 -s 2 { echo $it } | to json + "# + )); + + assert_eq!(actual.out, "[[1,2,3],[3,4,5]]"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn each_no_args_in_block() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [[foo bar]; [a b] [c d] [e f]] | each { to json } | nth 1 | str collect + "# + )); + + assert_eq!(actual.out, r#"{"foo":"c","bar":"d"}"#); +} + +#[test] +fn each_implicit_it_in_block() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [[foo bar]; [a b] [c d] [e f]] | each { nu --testbin cococo $it.foo } + "# + )); + + assert_eq!(actual.out, "ace"); +} diff --git a/crates/nu-command/tests/commands/echo.rs b/crates/nu-command/tests/commands/echo.rs new file mode 100644 index 0000000000..4da8e2dbec --- /dev/null +++ b/crates/nu-command/tests/commands/echo.rs @@ -0,0 +1,61 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn echo_range_is_lazy() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo 1..10000000000 | first 3 | to json --raw + "# + )); + + assert_eq!(actual.out, "[1,2,3]"); +} + +#[test] +fn echo_range_handles_inclusive() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo 1..3 | each { $it } | to json --raw + "# + )); + + assert_eq!(actual.out, "[1,2,3]"); +} + +#[test] +fn echo_range_handles_exclusive() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo 1..<3 | each { $it } | to json --raw + "# + )); + + assert_eq!(actual.out, "[1,2]"); +} + +#[test] +fn echo_range_handles_inclusive_down() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo 3..1 | each { $it } | to json --raw + "# + )); + + assert_eq!(actual.out, "[3,2,1]"); +} + +#[test] +fn echo_range_handles_exclusive_down() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo 3..<1 | each { $it } | to json --raw + "# + )); + + assert_eq!(actual.out, "[3,2]"); +} diff --git a/crates/nu-command/tests/commands/empty.rs b/crates/nu-command/tests/commands/empty.rs new file mode 100644 index 0000000000..22105ea282 --- /dev/null +++ b/crates/nu-command/tests/commands/empty.rs @@ -0,0 +1,94 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn reports_emptiness() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[are_empty]; + [([[check]; [[]] ])] + [([[check]; [""] ])] + [([[check]; [(wrap)] ])] + ] + | get are_empty + | empty? check + | where check + | length + "# + )); + + assert_eq!(actual.out, "3"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sets_block_run_value_for_an_empty_column() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ + [ first_name, last_name, rusty_at, likes ]; + [ Andrés, Robalino, 10/11/2013, 1 ] + [ Jonathan, Turner, 10/12/2013, 1 ] + [ Jason, Gedge, 10/11/2013, 1 ] + [ Yehuda, Katz, 10/11/2013, '' ] + ] + | empty? likes -b { 1 } + | get likes + | math sum + "# + )); + + assert_eq!(actual.out, "4"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sets_block_run_value_for_many_empty_columns() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ + [ boost check ]; + [ 1, [] ] + [ 1, "" ] + [ 1, (wrap) ] + ] + | empty? boost check -b { 1 } + | get boost check + | math sum + "# + )); + + assert_eq!(actual.out, "6"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn passing_a_block_will_set_contents_on_empty_cells_and_leave_non_empty_ones_untouched() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [ + [ NAME, LVL, HP ]; + [ Andrés, 30, 3000 ] + [ Alistair, 29, 2900 ] + [ Arepas, "", "" ] + [ Jorge, 30, 3000 ] + ] + | empty? LVL -b { 9 } + | empty? HP -b { + $it.LVL * 1000 + } + | math sum + | get HP + "# + )); + + assert_eq!(actual.out, "17900"); +} diff --git a/crates/nu-command/tests/commands/enter.rs b/crates/nu-command/tests/commands/enter.rs new file mode 100644 index 0000000000..945a740c8a --- /dev/null +++ b/crates/nu-command/tests/commands/enter.rs @@ -0,0 +1,73 @@ +use nu_test_support::fs::{files_exist_at, Stub::EmptyFile}; +use nu_test_support::nu; +use nu_test_support::playground::Playground; +use std::path::Path; + +#[test] +fn knows_the_filesystems_entered() { + Playground::setup("enter_test_1", |dirs, sandbox| { + sandbox + .within("red_pill") + .with_files(vec![ + EmptyFile("andres.nu"), + EmptyFile("jonathan.nu"), + EmptyFile("yehuda.nu"), + ]) + .within("blue_pill") + .with_files(vec![ + EmptyFile("bash.nxt"), + EmptyFile("korn.nxt"), + EmptyFile("powedsh.nxt"), + ]) + .mkdir("expected"); + + let red_pill_dir = dirs.test().join("red_pill"); + let blue_pill_dir = dirs.test().join("blue_pill"); + let expected = dirs.test().join("expected"); + let expected_recycled = expected.join("recycled"); + + nu!( + cwd: dirs.test(), + r#" + enter expected + mkdir recycled + enter ../red_pill + mv jonathan.nu ../expected + enter ../blue_pill + cp *.nxt ../expected/recycled + p + p + mv ../red_pill/yehuda.nu . + n + mv andres.nu ../expected/andres.nu + exit + cd .. + rm red_pill --recursive + exit + n + rm blue_pill --recursive + exit + "# + ); + + assert!(!red_pill_dir.exists()); + assert!(files_exist_at( + vec![ + Path::new("andres.nu"), + Path::new("jonathan.nu"), + Path::new("yehuda.nu"), + ], + expected + )); + + assert!(!blue_pill_dir.exists()); + assert!(files_exist_at( + vec![ + Path::new("bash.nxt"), + Path::new("korn.nxt"), + Path::new("powedsh.nxt"), + ], + expected_recycled + )); + }) +} diff --git a/crates/nu-command/tests/commands/every.rs b/crates/nu-command/tests/commands/every.rs new file mode 100644 index 0000000000..7fe925065c --- /dev/null +++ b/crates/nu-command/tests/commands/every.rs @@ -0,0 +1,211 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn gets_all_rows_by_every_zero() { + Playground::setup("every_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 0 + | to json --raw + "# + )); + + assert_eq!( + actual.out, + r#"["amigos.txt","arepas.clu","los.txt","tres.txt"]"# + ); + }) +} + +#[test] +fn gets_no_rows_by_every_skip_zero() { + Playground::setup("every_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 0 --skip + | to json --raw + "# + )); + + assert_eq!(actual.out, "[]"); + }) +} + +#[test] +fn gets_all_rows_by_every_one() { + Playground::setup("every_test_3", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 1 + | to json --raw + "# + )); + + assert_eq!( + actual.out, + r#"["amigos.txt","arepas.clu","los.txt","tres.txt"]"# + ); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn gets_no_rows_by_every_skip_one() { + Playground::setup("every_test_4", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 1 --skip + | to json --raw + "# + )); + + assert_eq!(actual.out, ""); + }) +} + +#[test] +fn gets_first_row_by_every_too_much() { + Playground::setup("every_test_5", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 999 + "# + )); + + let expected = nu!( + cwd: dirs.test(), pipeline( + r#" + echo [ amigos.txt ] + "# + )); + + assert_eq!(actual.out, expected.out); + }) +} + +#[test] +fn gets_all_rows_except_first_by_every_skip_too_much() { + Playground::setup("every_test_6", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 999 --skip + | to json --raw + "# + )); + + assert_eq!(actual.out, r#"["arepas.clu","los.txt","tres.txt"]"#); + }) +} + +#[test] +fn gets_every_third_row() { + Playground::setup("every_test_7", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("quatro.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 3 + | to json --raw + "# + )); + + assert_eq!(actual.out, r#"["amigos.txt","quatro.txt"]"#); + }) +} + +#[test] +fn skips_every_third_row() { + Playground::setup("every_test_8", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("quatro.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | every 3 --skip + | to json --raw + "# + )); + + assert_eq!(actual.out, r#"["arepas.clu","los.txt","tres.txt"]"#); + }) +} diff --git a/crates/nu-command/tests/commands/find.rs b/crates/nu-command/tests/commands/find.rs new file mode 100644 index 0000000000..959257ea9c --- /dev/null +++ b/crates/nu-command/tests/commands/find.rs @@ -0,0 +1,117 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn find_with_list_search_with_string() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [moe larry curly] | find moe + "# + )); + + assert_eq!(actual.out, "moe"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn find_with_list_search_with_char() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [moe larry curly] | find l | to json + "# + )); + + assert_eq!(actual.out, r#"["larry","curly"]"#); +} + +#[test] +fn find_with_list_search_with_number() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [1 2 3 4 5] | find 3 + "# + )); + + assert_eq!(actual.out, "3"); +} + +#[test] +fn find_with_string_search_with_string() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo Cargo.toml | find toml + "# + )); + + assert_eq!(actual.out, "Cargo.toml"); +} + +#[test] +fn find_with_string_search_with_string_not_found() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [moe larry curly] | find shemp + "# + )); + + assert_eq!(actual.out, ""); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn find_with_filepath_search_with_string() { + Playground::setup("filepath_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | find arep + | to json + "# + )); + + assert_eq!(actual.out, r#""arepas.clu""#); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn find_with_filepath_search_with_multiple_patterns() { + Playground::setup("filepath_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | find arep ami + | to json + "# + )); + + assert_eq!(actual.out, r#"["amigos.txt","arepas.clu"]"#); + }) +} diff --git a/crates/nu-command/tests/commands/first.rs b/crates/nu-command/tests/commands/first.rs new file mode 100644 index 0000000000..686fc6565e --- /dev/null +++ b/crates/nu-command/tests/commands/first.rs @@ -0,0 +1,67 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn gets_first_rows_by_amount() { + Playground::setup("first_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | first 3 + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn gets_all_rows_if_amount_higher_than_all_rows() { + Playground::setup("first_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | first 99 + | length + "# + )); + + assert_eq!(actual.out, "4"); + }) +} + +#[test] +fn gets_first_row_when_no_amount_given() { + Playground::setup("first_test_3", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("caballeros.txt"), EmptyFile("arepas.clu")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | first + | length + "# + )); + + assert_eq!(actual.out, "1"); + }) +} diff --git a/crates/nu-command/tests/commands/flatten.rs b/crates/nu-command/tests/commands/flatten.rs new file mode 100644 index 0000000000..3291bb34ef --- /dev/null +++ b/crates/nu-command/tests/commands/flatten.rs @@ -0,0 +1,192 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn flatten_nested_tables_with_columns() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[origin, people]; [Ecuador, ('Andres' | wrap name)]] + [[origin, people]; [Nu, ('nuno' | wrap name)]] + | flatten + | get name + | str collect ',' + "# + )); + + assert_eq!(actual.out, "Andres,nuno"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn flatten_nested_tables_that_have_many_columns() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[origin, people]; [Ecuador, (echo [[name, meal]; ['Andres', 'arepa']])]] + [[origin, people]; [USA, (echo [[name, meal]; ['Katz', 'nurepa']])]] + | flatten + | get meal + | str collect ',' + "# + )); + + assert_eq!(actual.out, "arepa,nurepa"); +} + +#[test] +fn flatten_nested_tables() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[Andrés, Nicolás, Robalino]] | flatten | nth 1 + "# + )); + + assert_eq!(actual.out, "Nicolás"); +} + +#[test] +fn flatten_row_column_explicitly() { + Playground::setup("flatten_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "katz.json", + r#" + [ + { + "people": { + "name": "Andres", + "meal": "arepa" + } + }, + { + "people": { + "name": "Katz", + "meal": "nurepa" + } + } + ] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open katz.json | flatten people | where name == Andres | length" + ); + + assert_eq!(actual.out, "1"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn flatten_row_columns_having_same_column_names_flats_separately() { + Playground::setup("flatten_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "katz.json", + r#" + [ + { + "people": { + "name": "Andres", + "meal": "arepa" + }, + "city": [{"name": "Guayaquil"}, {"name": "Samborondón"}] + }, + { + "people": { + "name": "Katz", + "meal": "nurepa" + }, + "city": [{"name": "Oregon"}, {"name": "Brooklin"}] + } + ] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open katz.json | flatten | flatten people city | get city_name | length" + ); + + assert_eq!(actual.out, "4"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn flatten_table_columns_explicitly() { + Playground::setup("flatten_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "katz.json", + r#" + [ + { + "people": { + "name": "Andres", + "meal": "arepa" + }, + "city": ["Guayaquil", "Samborondón"] + }, + { + "people": { + "name": "Katz", + "meal": "nurepa" + }, + "city": ["Oregon", "Brooklin"] + } + ] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open katz.json | flatten city | where people.name == Katz | length" + ); + + assert_eq!(actual.out, "2"); + }) +} + +#[test] +fn flatten_more_than_one_column_that_are_subtables_not_supported() { + Playground::setup("flatten_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "katz.json", + r#" + [ + { + "people": { + "name": "Andres", + "meal": "arepa" + } + "tags": ["carbohydrate", "corn", "maiz"], + "city": ["Guayaquil", "Samborondón"] + }, + { + "people": { + "name": "Katz", + "meal": "nurepa" + }, + "tags": ["carbohydrate", "shell food", "amigos flavor"], + "city": ["Oregon", "Brooklin"] + } + ] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open katz.json | flatten tags city" + ); + + assert!(actual.err.contains("tried flattening")); + assert!(actual.err.contains("but is flattened already")); + }) +} diff --git a/crates/nu-command/tests/commands/format.rs b/crates/nu-command/tests/commands/format.rs new file mode 100644 index 0000000000..58766393e0 --- /dev/null +++ b/crates/nu-command/tests/commands/format.rs @@ -0,0 +1,101 @@ +use nu_test_support::fs::Stub::{EmptyFile, FileWithContentToBeTrimmed}; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn creates_the_resulting_string_from_the_given_fields() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml + | get package + | format "{name} has license {license}" + "# + )); + + assert_eq!(actual.out, "nu has license ISC"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn given_fields_can_be_column_paths() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml + | format "{package.name} is {package.description}" + "# + )); + + assert_eq!(actual.out, "nu is a new type of shell"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn can_use_variables() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml + | format "{$it.package.name} is {$it.package.description}" + "# + )); + + assert_eq!(actual.out, "nu is a new type of shell"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn format_filesize_works() { + Playground::setup("format_filesize_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | format filesize size KB + | get size + | first + "# + )); + + assert_eq!(actual.out, "0.0 KB"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn format_filesize_works_with_nonempty_files() { + Playground::setup( + "format_filesize_works_with_nonempty_files", + |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.toml", + r#" + [dependency] + name = "nu" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "ls sample.toml | format filesize size B | get size | first" + ); + + #[cfg(not(windows))] + assert_eq!(actual.out, "25"); + + #[cfg(windows)] + assert_eq!(actual.out, "27"); + }, + ) +} diff --git a/crates/nu-command/tests/commands/get.rs b/crates/nu-command/tests/commands/get.rs new file mode 100644 index 0000000000..01d98321d7 --- /dev/null +++ b/crates/nu-command/tests/commands/get.rs @@ -0,0 +1,229 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn fetches_a_row() { + Playground::setup("get_test_1", |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 + | get nu_party_venue + "# + )); + + assert_eq!(actual.out, "zion"); + }) +} + +#[test] +fn fetches_by_index() { + Playground::setup("get_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nu" + version = "0.4.1" + authors = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] + description = "When arepas shells are tasty and fun." + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get package.authors.2 + "# + )); + + assert_eq!(actual.out, "Andrés N. Robalino "); + }) +} +#[test] +fn fetches_by_column_path() { + Playground::setup("get_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nu" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get package.name + "# + )); + + assert_eq!(actual.out, "nu"); + }) +} + +#[test] +fn column_paths_are_either_double_quoted_or_regular_unquoted_words_separated_by_dot() { + Playground::setup("get_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + 9999 = ["Yehuda Katz ", "Jonathan Turner ", "Andrés N. Robalino "] + description = "When arepas shells are tasty and fun." + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get package."9999" + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn fetches_more_than_one_column_path() { + Playground::setup("get_test_5", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [[fortune_tellers]] + name = "Andrés N. Robalino" + arepas = 1 + + [[fortune_tellers]] + name = "Jonathan Turner" + arepas = 1 + + [[fortune_tellers]] + name = "Yehuda Katz" + arepas = 1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get fortune_tellers.2.name fortune_tellers.0.name fortune_tellers.1.name + | nth 2 + "# + )); + + assert_eq!(actual.out, "Jonathan Turner"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_fetching_by_column_not_present() { + Playground::setup("get_test_6", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [taconushell] + sentence_words = ["Yo", "quiero", "taconushell"] + [pizzanushell] + sentence-words = ["I", "want", "pizza"] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get taco + "# + )); + + assert!(actual.err.contains("Unknown column"),); + assert!(actual.err.contains("There isn't a column named 'taco'"),); + assert!(actual.err.contains("Perhaps you meant 'taconushell'?"),); + assert!(actual + .err + .contains("Columns available: pizzanushell, taconushell"),); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_fetching_by_column_using_a_number() { + Playground::setup("get_test_7", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [spanish_lesson] + 0 = "can only be fetched with 0 double quoted." + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get spanish_lesson.0 + "# + )); + + assert!(actual.err.contains("No rows available"),); + assert!(actual.err.contains("A row at '0' can't be indexed."),); + assert!(actual + .err + .contains("Appears to contain columns. Columns available: 0"),) + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_fetching_by_index_out_of_bounds() { + Playground::setup("get_test_8", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [spanish_lesson] + sentence_words = ["Yo", "quiero", "taconushell"] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get spanish_lesson.sentence_words.3 + "# + )); + + assert!(actual.err.contains("Row not found"),); + assert!(actual.err.contains("There isn't a row indexed at 3"),); + assert!(actual.err.contains("The table only has 3 rows (0 to 2)"),) + }) +} + +#[test] +fn quoted_column_access() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r#"echo '[{"foo bar": {"baz": 4}}]' | from json | get "foo bar".baz "# + ); + + assert_eq!(actual.out, "4"); +} diff --git a/crates/nu-command/tests/commands/group_by.rs b/crates/nu-command/tests/commands/group_by.rs new file mode 100644 index 0000000000..05f8fe9902 --- /dev/null +++ b/crates/nu-command/tests/commands/group_by.rs @@ -0,0 +1,99 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn groups() { + Playground::setup("group_by_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at,type + Andrés,Robalino,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | group-by rusty_at + | get "10/11/2013" + | length + "# + )); + + assert_eq!(actual.out, "2"); + }) +} + +#[test] +fn errors_if_given_unknown_column_name() { + Playground::setup("group_by_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.json", + r#" + { + "nu": { + "committers": [ + {"name": "Andrés N. Robalino"}, + {"name": "Jonathan Turner"}, + {"name": "Yehuda Katz"} + ], + "releases": [ + {"version": "0.2"} + {"version": "0.8"}, + {"version": "0.9999999"} + ], + "0xATYKARNU": [ + ["Th", "e", " "], + ["BIG", " ", "UnO"], + ["punto", "cero"] + ] + } + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.json + | group-by { get nu.releases.version } + "# + )); + + assert!(actual + .err + .contains("requires a table with one value for grouping")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_block_given_evaluates_more_than_one_row() { + Playground::setup("group_by_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at,type + Andrés,Robalino,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | group-by ttype + "# + )); + + assert!(actual.err.contains("Unknown column")); + }) +} diff --git a/crates/nu-command/tests/commands/hash_/mod.rs b/crates/nu-command/tests/commands/hash_/mod.rs new file mode 100644 index 0000000000..bd8a53bcfd --- /dev/null +++ b/crates/nu-command/tests/commands/hash_/mod.rs @@ -0,0 +1,122 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn base64_defaults_to_encoding_with_standard_character_type() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 'username:password' | hash base64 + "# + ) + ); + + assert_eq!(actual.out, "dXNlcm5hbWU6cGFzc3dvcmQ="); +} + +#[test] +fn base64_encode_characterset_binhex() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 'username:password' | hash base64 --character_set binhex --encode + "# + ) + ); + + assert_eq!(actual.out, "F@0NEPjJD97kE\'&bEhFZEP3"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn error_when_invalid_character_set_given() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 'username:password' | hash base64 --character_set 'this is invalid' --encode + "# + ) + ); + + assert!(actual + .err + .contains("this is invalid is not a valid character-set")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn base64_decode_characterset_binhex() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "F@0NEPjJD97kE'&bEhFZEP3" | hash base64 --character_set binhex --decode + "# + ) + ); + + assert_eq!(actual.out, "username:password"); +} + +#[test] +fn error_invalid_decode_value() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "this should not be a valid encoded value" | hash base64 --character_set url-safe --decode + "# + ) + ); + + assert!(actual + .err + .contains("invalid base64 input for character set url-safe")); +} + +#[test] +fn error_use_both_flags() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 'username:password' | hash base64 --encode --decode + "# + ) + ); + + assert!(actual + .err + .contains("only one of --decode and --encode flags can be used")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn md5_works_with_file() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db | hash md5 + "# + ) + ); + + assert_eq!(actual.out, "4de97601d232c427977ef11db396c951"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sha256_works_with_file() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db | hash sha256 + "# + ) + ); + + assert_eq!( + actual.out, + "2f5050e7eea415c1f3d80b5d93355efd15043ec9157a2bb167a9e73f2ae651f2" + ); +} diff --git a/crates/nu-command/tests/commands/headers.rs b/crates/nu-command/tests/commands/headers.rs new file mode 100644 index 0000000000..b90bcad1ee --- /dev/null +++ b/crates/nu-command/tests/commands/headers.rs @@ -0,0 +1,35 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn headers_uses_first_row_as_header() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample_headers.xlsx + | get Sheet1 + | headers + | get header0 + | from json"# + )); + + assert_eq!(actual.out, "r1c0r2c0") +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn headers_adds_missing_column_name() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample_headers.xlsx + | get Sheet1 + | headers + | get Column1 + | from json"# + )); + + assert_eq!(actual.out, "r1c1r2c1") +} diff --git a/crates/nu-command/tests/commands/help.rs b/crates/nu-command/tests/commands/help.rs new file mode 100644 index 0000000000..0105318725 --- /dev/null +++ b/crates/nu-command/tests/commands/help.rs @@ -0,0 +1,33 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn help_commands_length() { + let actual = nu!( + cwd: ".", pipeline( + r#" + help commands | length + "# + )); + + let output = actual.out; + let output_int: i32 = output.parse().unwrap(); + let is_positive = output_int.is_positive(); + assert!(is_positive); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn help_generate_docs_length() { + let actual = nu!( + cwd: ".", pipeline( + r#" + help generate_docs | flatten | length + "# + )); + + let output = actual.out; + let output_int: i32 = output.parse().unwrap(); + let is_positive = output_int.is_positive(); + assert!(is_positive); +} diff --git a/crates/nu-command/tests/commands/histogram.rs b/crates/nu-command/tests/commands/histogram.rs new file mode 100644 index 0000000000..1b799f0f37 --- /dev/null +++ b/crates/nu-command/tests/commands/histogram.rs @@ -0,0 +1,117 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn summarizes_by_column_given() { + Playground::setup("histogram_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at + Andrés,Robalino,Ecuador + Jonathan,Turner,Estados Unidos + Yehuda,Katz,Estados Unidos + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | histogram rusty_at countries + | where rusty_at == "Ecuador" + | get countries + "# + )); + + assert_eq!( + actual.out, + "**************************************************" + ); + // 50% + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn summarizes_by_values() { + Playground::setup("histogram_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at + Andrés,Robalino,Ecuador + Jonathan,Turner,Estados Unidos + Yehuda,Katz,Estados Unidos + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | get rusty_at + | histogram + | where value == "Estados Unidos" + | get count + "# + )); + + assert_eq!(actual.out, "2"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn help() { + Playground::setup("histogram_test_3", |dirs, _sandbox| { + let help_command = nu!( + cwd: dirs.test(), pipeline( + r#" + help histogram + "# + )); + + let help_short = nu!( + cwd: dirs.test(), pipeline( + r#" + histogram -h + "# + )); + + let help_long = nu!( + cwd: dirs.test(), pipeline( + r#" + histogram --help + "# + )); + + assert_eq!(help_short.out, help_command.out); + assert_eq!(help_long.out, help_command.out); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn count() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[bit]; [1] [0] [0] [0] [0] [0] [0] [1]] + | histogram bit + | sort-by count + | reject frequency + | to json + "# + )); + + let bit_json = r#"[{"bit":"1","count":2,"percentage":"33.33%"},{"bit":"0","count":6,"percentage":"100.00%"}]"#; + + assert_eq!(actual.out, bit_json); +} diff --git a/crates/nu-command/tests/commands/into_filesize.rs b/crates/nu-command/tests/commands/into_filesize.rs new file mode 100644 index 0000000000..d57665613c --- /dev/null +++ b/crates/nu-command/tests/commands/into_filesize.rs @@ -0,0 +1,80 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn into_filesize_int() { + let actual = nu!( + cwd: ".", pipeline( + r#" + 1 | into filesize + "# + )); + + assert!(actual.out.contains("1 B")); +} + +#[test] +fn into_filesize_decimal() { + let actual = nu!( + cwd: ".", pipeline( + r#" + 1.2 | into filesize + "# + )); + + assert!(actual.out.contains("1 B")); +} + +#[test] +fn into_filesize_str() { + let actual = nu!( + cwd: ".", pipeline( + r#" + '2000' | into filesize + "# + )); + + assert!(actual.out.contains("2.0 KiB")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn into_filesize_str_newline() { + let actual = nu!( + cwd: ".", pipeline( + r#" + "2000 +" | into filesize + "# + )); + + assert!(actual.out.contains("2.0 KiB")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn into_filesize_str_many_newlines() { + let actual = nu!( + cwd: ".", pipeline( + r#" + "2000 + +" | into filesize + "# + )); + + assert!(actual.out.contains("2.0 KiB")); +} + +#[test] +fn into_filesize_filesize() { + let actual = nu!( + cwd: ".", pipeline( + r#" + 3kib | into filesize + "# + )); + + assert!(actual.out.contains("3.0 KiB")); +} diff --git a/crates/nu-command/tests/commands/into_int.rs b/crates/nu-command/tests/commands/into_int.rs new file mode 100644 index 0000000000..dbb20a1c7d --- /dev/null +++ b/crates/nu-command/tests/commands/into_int.rs @@ -0,0 +1,37 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn into_int_filesize() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1kb | into int | each { $it / 1000 } + "# + )); + + assert!(actual.out.contains('1')); +} + +#[test] +fn into_int_filesize2() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1kib | into int | each { $it / 1024 } + "# + )); + + assert!(actual.out.contains('1')); +} + +#[test] +fn into_int_int() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1024 | into int | each { $it / 1024 } + "# + )); + + assert!(actual.out.contains('1')); +} diff --git a/crates/nu-command/tests/commands/keep/mod.rs b/crates/nu-command/tests/commands/keep/mod.rs new file mode 100644 index 0000000000..a267c23529 --- /dev/null +++ b/crates/nu-command/tests/commands/keep/mod.rs @@ -0,0 +1,3 @@ +mod rows; +mod until; +mod while_; diff --git a/crates/nu-command/tests/commands/keep/rows.rs b/crates/nu-command/tests/commands/keep/rows.rs new file mode 100644 index 0000000000..84df113770 --- /dev/null +++ b/crates/nu-command/tests/commands/keep/rows.rs @@ -0,0 +1,31 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn rows() { + Playground::setup("keep_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "caballeros.csv", + r#" + name,lucky_code + Andrés,1 + Jonathan,1 + Jason,2 + Yehuda,1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open caballeros.csv + | keep 3 + | get lucky_code + | math sum + "# + )); + + assert_eq!(actual.out, "4"); + }) +} diff --git a/crates/nu-command/tests/commands/keep/until.rs b/crates/nu-command/tests/commands/keep/until.rs new file mode 100644 index 0000000000..b878e720ab --- /dev/null +++ b/crates/nu-command/tests/commands/keep/until.rs @@ -0,0 +1,52 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn condition_is_met() { + Playground::setup("keep_until_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "caballeros.txt", + r#" + CHICKEN SUMMARY report date: April 29th, 2020 + -------------------------------------------------------------------- + Chicken Collection,29/04/2020,30/04/2020,31/04/2020 + Yellow Chickens,,, + Andrés,1,1,1 + Jonathan,1,1,1 + Jason,1,1,1 + Yehuda,1,1,1 + Blue Chickens,,, + Andrés,1,1,2 + Jonathan,1,1,2 + Jason,1,1,2 + Yehuda,1,1,2 + Red Chickens,,, + Andrés,1,1,3 + Jonathan,1,1,3 + Jason,1,1,3 + Yehuda,1,1,3 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open --raw caballeros.txt + | lines + | skip 2 + | str trim + | str collect (char nl) + | from csv + | skip while "Chicken Collection" != "Blue Chickens" + | keep until "Chicken Collection" == "Red Chickens" + | skip 1 + | into int "31/04/2020" + | get "31/04/2020" + | math sum + "# + )); + + assert_eq!(actual.out, "8"); + }) +} diff --git a/crates/nu-command/tests/commands/keep/while_.rs b/crates/nu-command/tests/commands/keep/while_.rs new file mode 100644 index 0000000000..72819e6c04 --- /dev/null +++ b/crates/nu-command/tests/commands/keep/while_.rs @@ -0,0 +1,51 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn condition_is_met() { + Playground::setup("keep_while_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "caballeros.txt", + r#" + CHICKEN SUMMARY report date: April 29th, 2020 + -------------------------------------------------------------------- + Chicken Collection,29/04/2020,30/04/2020,31/04/2020 + Yellow Chickens,,, + Andrés,1,1,1 + Jonathan,1,1,1 + Jason,1,1,1 + Yehuda,1,1,1 + Blue Chickens,,, + Andrés,1,1,2 + Jonathan,1,1,2 + Jason,1,1,2 + Yehuda,1,1,2 + Red Chickens,,, + Andrés,1,1,3 + Jonathan,1,1,3 + Jason,1,1,3 + Yehuda,1,1,3 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open --raw caballeros.txt + | lines + | skip 2 + | str trim + | str collect (char nl) + | from csv + | skip 1 + | keep while "Chicken Collection" != "Blue Chickens" + | into int "31/04/2020" + | get "31/04/2020" + | math sum + "# + )); + + assert_eq!(actual.out, "4"); + }) +} diff --git a/crates/nu-command/tests/commands/last.rs b/crates/nu-command/tests/commands/last.rs new file mode 100644 index 0000000000..b5acbb99ec --- /dev/null +++ b/crates/nu-command/tests/commands/last.rs @@ -0,0 +1,66 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn gets_the_last_row() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "ls | sort-by name | last 1 | get name | str trim" + ); + + assert_eq!(actual.out, "utf16.ini"); +} + +#[test] +fn gets_last_rows_by_amount() { + Playground::setup("last_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | last 3 + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn gets_last_row_when_no_amount_given() { + Playground::setup("last_test_2", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("caballeros.txt"), EmptyFile("arepas.clu")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | last + | length + "# + )); + + assert_eq!(actual.out, "1"); + }) +} + +#[test] +fn requests_more_rows_than_table_has() { + let actual = nu!( + cwd: ".", pipeline( + r#" + date | last 50 | length + "# + )); + + assert_eq!(actual.out, "1"); +} diff --git a/crates/nu-command/tests/commands/length.rs b/crates/nu-command/tests/commands/length.rs new file mode 100644 index 0000000000..9c5f51e9d7 --- /dev/null +++ b/crates/nu-command/tests/commands/length.rs @@ -0,0 +1,25 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn length_columns_in_cal_table() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cal | length -c + "# + )); + + assert_eq!(actual.out, "7"); +} + +#[test] +fn length_columns_no_rows() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [] | length -c + "# + )); + + assert_eq!(actual.out, "0"); +} diff --git a/crates/nu-command/tests/commands/lines.rs b/crates/nu-command/tests/commands/lines.rs new file mode 100644 index 0000000000..e9fbca386e --- /dev/null +++ b/crates/nu-command/tests/commands/lines.rs @@ -0,0 +1,54 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn lines() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml -r + | lines + | skip while $it != "[dependencies]" + | skip 1 + | first 1 + | split column "=" + | get Column1 + | str trim + "# + )); + + assert_eq!(actual.out, "rustyline"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn lines_proper_buffering() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open lines_test.txt -r + | lines + | str length + | to json + "# + )); + + assert_eq!(actual.out, "[8193,3]"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn lines_multi_value_split() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample-simple.json + | get first second + | lines + | length + "# + )); + + assert_eq!(actual.out, "5"); +} diff --git a/crates/nu-command/tests/commands/ls.rs b/crates/nu-command/tests/commands/ls.rs new file mode 100644 index 0000000000..eda52a6cda --- /dev/null +++ b/crates/nu-command/tests/commands/ls.rs @@ -0,0 +1,352 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn lists_regular_files() { + Playground::setup("ls_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn lists_regular_files_using_asterisk_wildcard() { + Playground::setup("ls_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls *.txt + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn lists_regular_files_using_question_mark_wildcard() { + Playground::setup("ls_test_3", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.10.txt"), + EmptyFile("jonathan.10.txt"), + EmptyFile("andres.10.txt"), + EmptyFile("chicken_not_to_be_picked_up.100.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls *.??.txt + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn lists_all_files_in_directories_from_stream() { + Playground::setup("ls_test_4", |dirs, sandbox| { + sandbox + .with_files(vec![EmptyFile("root1.txt"), EmptyFile("root2.txt")]) + .within("dir_a") + .with_files(vec![ + EmptyFile("yehuda.10.txt"), + EmptyFile("jonathan.10.txt"), + ]) + .within("dir_b") + .with_files(vec![ + EmptyFile("andres.10.txt"), + EmptyFile("chicken_not_to_be_picked_up.100.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo dir_a dir_b + | each { ls $it } + | length + "# + )); + + assert_eq!(actual.out, "4"); + }) +} + +#[test] +fn does_not_fail_if_glob_matches_empty_directory() { + Playground::setup("ls_test_5", |dirs, sandbox| { + sandbox.within("dir_a"); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls dir_a + | length + "# + )); + + assert_eq!(actual.out, "0"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn fails_when_glob_doesnt_match() { + Playground::setup("ls_test_5", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("root1.txt"), EmptyFile("root2.txt")]); + + let actual = nu!( + cwd: dirs.test(), + "ls root3*" + ); + + assert!(actual.err.contains("no matches found")); + }) +} + +#[test] +fn list_files_from_two_parents_up_using_multiple_dots() { + Playground::setup("ls_test_6", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yahuda.yaml"), + EmptyFile("jonathan.json"), + EmptyFile("andres.xml"), + EmptyFile("kevin.txt"), + ]); + + sandbox.within("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test().join("foo/bar"), + r#" + ls ... | length + "# + ); + + assert_eq!(actual.out, "5"); + }) +} + +#[test] +fn lists_hidden_file_when_explicitly_specified() { + Playground::setup("ls_test_7", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + EmptyFile(".testdotfile"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls .testdotfile + | length + "# + )); + + assert_eq!(actual.out, "1"); + }) +} + +#[test] +fn lists_all_hidden_files_when_glob_contains_dot() { + Playground::setup("ls_test_8", |dirs, sandbox| { + sandbox + .with_files(vec![ + EmptyFile("root1.txt"), + EmptyFile("root2.txt"), + EmptyFile(".dotfile1"), + ]) + .within("dir_a") + .with_files(vec![ + EmptyFile("yehuda.10.txt"), + EmptyFile("jonathan.10.txt"), + EmptyFile(".dotfile2"), + ]) + .within("dir_b") + .with_files(vec![ + EmptyFile("andres.10.txt"), + EmptyFile("chicken_not_to_be_picked_up.100.txt"), + EmptyFile(".dotfile3"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls **/.* + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +// TODO Remove this cfg value when we have an OS-agnostic way +// of creating hidden files using the playground. +#[cfg(unix)] +fn lists_all_hidden_files_when_glob_does_not_contain_dot() { + Playground::setup("ls_test_8", |dirs, sandbox| { + sandbox + .with_files(vec![ + EmptyFile("root1.txt"), + EmptyFile("root2.txt"), + EmptyFile(".dotfile1"), + ]) + .within("dir_a") + .with_files(vec![ + EmptyFile("yehuda.10.txt"), + EmptyFile("jonathan.10.txt"), + EmptyFile(".dotfile2"), + ]) + .within(".dir_b") + .with_files(vec![ + EmptyFile("andres.10.txt"), + EmptyFile("chicken_not_to_be_picked_up.100.txt"), + EmptyFile(".dotfile3"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls **/* + | length + "# + )); + + assert_eq!(actual.out, "5"); + }) +} + +#[test] +#[cfg(unix)] +fn fails_with_ls_to_dir_without_permission() { + Playground::setup("ls_test_1", |dirs, sandbox| { + sandbox.within("dir_a").with_files(vec![ + EmptyFile("yehuda.11.txt"), + EmptyFile("jonathan.10.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + chmod 000 dir_a; ls dir_a + "# + )); + assert!(actual + .err + .contains("The permissions of 0 do not allow access for this user")); + }) +} + +#[test] +fn lists_files_including_starting_with_dot() { + Playground::setup("ls_test_9", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + EmptyFile(".hidden1.txt"), + EmptyFile(".hidden2.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls -a + | length + "# + )); + + assert_eq!(actual.out, "5"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn list_all_columns() { + Playground::setup("ls_test_all_columns", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("Leonardo.yaml"), + EmptyFile("Raphael.json"), + EmptyFile("Donatello.xml"), + EmptyFile("Michelangelo.txt"), + ]); + // Normal Operation + let actual = nu!( + cwd: dirs.test(), + "ls | get | to md" + ); + let expected = ["name", "type", "size", "modified"].join(""); + assert_eq!(actual.out, expected, "column names are incorrect for ls"); + // Long + let actual = nu!( + cwd: dirs.test(), + "ls -l | get | to md" + ); + let expected = { + #[cfg(unix)] + { + [ + "name", + "type", + "target", + "num_links", + "inode", + "readonly", + "mode", + "uid", + "group", + "size", + "created", + "accessed", + "modified", + ] + .join("") + } + + #[cfg(windows)] + { + [ + "name", "type", "target", "readonly", "size", "created", "accessed", "modified", + ] + .join("") + } + }; + assert_eq!( + actual.out, expected, + "column names are incorrect for ls long" + ); + }); +} diff --git a/crates/nu-command/tests/commands/math/avg.rs b/crates/nu-command/tests/commands/math/avg.rs new file mode 100644 index 0000000000..e3351c3702 --- /dev/null +++ b/crates/nu-command/tests/commands/math/avg.rs @@ -0,0 +1,27 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn can_average_numbers() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sgml_description.json + | get glossary.GlossDiv.GlossList.GlossEntry.Sections + | math avg + "# + )); + + assert_eq!(actual.out, "101.5") +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn can_average_bytes() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "ls | sort-by name | skip 1 | first 2 | get size | math avg | format \"{$it}\" " + ); + + assert_eq!(actual.out, "1.6 KB"); +} diff --git a/crates/nu-command/tests/commands/math/eval.rs b/crates/nu-command/tests/commands/math/eval.rs new file mode 100644 index 0000000000..14c83c6ea6 --- /dev/null +++ b/crates/nu-command/tests/commands/math/eval.rs @@ -0,0 +1,93 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn evaluates_two_plus_two() { + let actual = nu!( + cwd: ".", pipeline( + r#" + math eval "2 + 2" + "# + )); + + assert!(actual.out.contains("4.0")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn evaluates_two_to_the_power_four() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "2 ^ 4" | math eval + "# + )); + + assert!(actual.out.contains("16.0")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn evaluates_three_multiplied_by_five() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "3 * 5" | math eval + "# + )); + + assert!(actual.out.contains("15.0")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn evaluates_twenty_four_divided_by_two() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "24 / 2" | math eval + "# + )); + + assert!(actual.out.contains("12.0")); +} + +#[test] +fn evaluates_twenty_eight_minus_seven() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "28 - 7" | math eval + "# + )); + + assert!(actual.out.contains("21")); +} + +#[test] +fn evaluates_pi() { + let actual = nu!( + cwd: ".", pipeline( + r#" + math eval pi + "# + )); + + assert!(actual.out.contains("3.14")); +} + +#[test] +fn evaluates_tau() { + let actual = nu!( + cwd: ".", pipeline( + r#" + math eval tau + "# + )); + + assert!(actual.out.contains("6.28")); +} diff --git a/crates/nu-command/tests/commands/math/median.rs b/crates/nu-command/tests/commands/math/median.rs new file mode 100644 index 0000000000..7509fe8f43 --- /dev/null +++ b/crates/nu-command/tests/commands/math/median.rs @@ -0,0 +1,40 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn median_numbers_with_even_rows() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [10 6 19 21 4] + | math median + "# + )); + + assert_eq!(actual.out, "10") +} + +#[test] +fn median_numbers_with_odd_rows() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [3 8 9 12 12 15] + | math median + "# + )); + + assert_eq!(actual.out, "10.5") +} + +#[test] +fn median_mixed_numbers() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [-11.5 -13.5 10] + | math median + "# + )); + + assert_eq!(actual.out, "-11.5") +} diff --git a/crates/nu-command/tests/commands/math/mod.rs b/crates/nu-command/tests/commands/math/mod.rs new file mode 100644 index 0000000000..23193d83d0 --- /dev/null +++ b/crates/nu-command/tests/commands/math/mod.rs @@ -0,0 +1,288 @@ +mod avg; +mod eval; +mod median; +mod round; +mod sqrt; +mod sum; + +use nu_test_support::{nu, pipeline}; + +#[test] +fn one_arg() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 + "# + )); + + assert_eq!(actual.out, "1"); +} + +#[test] +fn add() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 + 1 + "# + )); + + assert_eq!(actual.out, "2"); +} + +#[test] +fn add_compound() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 + 2 + 2 + "# + )); + + assert_eq!(actual.out, "5"); +} + +#[test] +fn precedence_of_operators() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 + 2 * 2 + "# + )); + + assert_eq!(actual.out, "5"); +} + +#[test] +fn precedence_of_operators2() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 + 2 * 2 + 1 + "# + )); + + assert_eq!(actual.out, "6"); +} + +#[test] +fn division_of_ints() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 4 / 2 + "# + )); + + assert_eq!(actual.out, "2"); +} + +#[test] +fn division_of_ints2() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 / 4 + "# + )); + + assert_eq!(actual.out, "0.25"); +} + +#[test] +fn error_zero_division_int_int() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 / 0 + "# + )); + + assert!(actual.err.contains("division by zero")); +} + +#[test] +fn error_zero_division_decimal_int() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1.0 / 0 + "# + )); + + assert!(actual.err.contains("division by zero")); +} + +#[test] +fn error_zero_division_int_decimal() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1 / 0.0 + "# + )); + + assert!(actual.err.contains("division by zero")); +} + +#[test] +fn error_zero_division_decimal_decimal() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1.0 / 0.0 + "# + )); + + assert!(actual.err.contains("division by zero")); +} + +#[test] +fn proper_precedence_history() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 2 / 2 / 2 + 1 + "# + )); + + assert_eq!(actual.out, "1.5"); +} + +#[test] +fn parens_precedence() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 4 * (6 - 3) + "# + )); + + assert_eq!(actual.out, "12"); +} + +#[test] +fn modulo() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 9 mod 2 + "# + )); + + assert_eq!(actual.out, "1"); +} + +#[test] +fn duration_math() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1wk + 1day + "# + )); + + assert_eq!(actual.out, "8day"); +} + +#[test] +fn duration_decimal_math() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 5.5day + 0.5day + "# + )); + + assert_eq!(actual.out, "6day"); +} + +#[test] +fn duration_math_with_nanoseconds() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1wk + 10ns + "# + )); + + assert_eq!(actual.out, "7day 10ns"); +} + +#[test] +fn duration_decimal_math_with_nanoseconds() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1.5wk + 10ns + "# + )); + + assert_eq!(actual.out, "10day 10ns"); +} + +#[test] +fn duration_math_with_negative() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 1day - 1wk + "# + )); + + assert_eq!(actual.out, "-6day"); +} + +#[test] +fn compound_comparison() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 4 > 3 && 2 > 1 + "# + )); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn compound_comparison2() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + 4 < 3 || 2 > 1 + "# + )); + + assert_eq!(actual.out, "true"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn compound_where() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo '[{"a": 1, "b": 1}, {"a": 2, "b": 1}, {"a": 2, "b": 2}]' | from json | where a == 2 && b == 1 | to json + "# + )); + + assert_eq!(actual.out, r#"{"a":2,"b":1}"#); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn compound_where_paren() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo '[{"a": 1, "b": 1}, {"a": 2, "b": 1}, {"a": 2, "b": 2}]' | from json | where ($it.a == 2 && $it.b == 1) || $it.b == 2 | to json + "# + )); + + assert_eq!(actual.out, r#"[{"a":2,"b":1},{"a":2,"b":2}]"#); +} diff --git a/crates/nu-command/tests/commands/math/round.rs b/crates/nu-command/tests/commands/math/round.rs new file mode 100644 index 0000000000..2bec436a64 --- /dev/null +++ b/crates/nu-command/tests/commands/math/round.rs @@ -0,0 +1,21 @@ +use nu_test_support::nu; + +#[test] +fn can_round_very_large_numbers() { + let actual = nu!( + cwd: ".", + "echo 18.1372544780074142289927665486772012345 | math round" + ); + + assert_eq!(actual.out, "18") +} + +#[test] +fn can_round_very_large_numbers_with_precision() { + let actual = nu!( + cwd: ".", + "echo 18.13725447800741422899276654867720121457878988 | math round -p 10" + ); + + assert_eq!(actual.out, "18.137254478") +} diff --git a/crates/nu-command/tests/commands/math/sqrt.rs b/crates/nu-command/tests/commands/math/sqrt.rs new file mode 100644 index 0000000000..7d779d2354 --- /dev/null +++ b/crates/nu-command/tests/commands/math/sqrt.rs @@ -0,0 +1,35 @@ +use nu_test_support::nu; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn can_sqrt_numbers() { + let actual = nu!( + cwd: ".", + "echo [0.25 2 4] | math sqrt | math sum" + ); + + assert_eq!(actual.out, "3.914213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn can_sqrt_irrational() { + let actual = nu!( + cwd: ".", + "echo 2 | math sqrt" + ); + + assert_eq!(actual.out, "1.414213562373095048801688724209698078569671875376948073176679737990732478462107038850387534327641573"); +} + +#[test] +fn can_sqrt_perfect_square() { + let actual = nu!( + cwd: ".", + "echo 4 | math sqrt" + ); + + assert_eq!(actual.out, "2"); +} diff --git a/crates/nu-command/tests/commands/math/sum.rs b/crates/nu-command/tests/commands/math/sum.rs new file mode 100644 index 0000000000..bb78e91e16 --- /dev/null +++ b/crates/nu-command/tests/commands/math/sum.rs @@ -0,0 +1,89 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; +use std::str::FromStr; + +#[test] +fn all() { + Playground::setup("sum_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "meals.json", + r#" + { + meals: [ + {description: "1 large egg", calories: 90}, + {description: "1 cup white rice", calories: 250}, + {description: "1 tablespoon fish oil", calories: 108} + ] + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open meals.json + | get meals + | get calories + | math sum + "# + )); + + assert_eq!(actual.out, "448"); + }) +} + +#[test] +#[allow(clippy::unreadable_literal)] +#[allow(clippy::float_cmp)] +fn compute_sum_of_individual_row() -> Result<(), String> { + let answers_for_columns = [ + ("cpu", 88.257434), + ("mem", 3032375296.), + ("virtual", 102579965952.), + ]; + for (column_name, expected_value) in answers_for_columns { + let actual = nu!( + cwd: "tests/fixtures/formats/", + format!("open sample-ps-output.json | select {} | math sum | get {}", column_name, column_name) + ); + let result = + f64::from_str(&actual.out).map_err(|_| String::from("Failed to parse float."))?; + assert_eq!(result, expected_value); + } + Ok(()) +} + +#[test] +#[allow(clippy::unreadable_literal)] +#[allow(clippy::float_cmp)] +fn compute_sum_of_table() -> Result<(), String> { + let answers_for_columns = [ + ("cpu", 88.257434), + ("mem", 3032375296.), + ("virtual", 102579965952.), + ]; + for (column_name, expected_value) in answers_for_columns { + let actual = nu!( + cwd: "tests/fixtures/formats/", + format!("open sample-ps-output.json | select cpu mem virtual | math sum | get {}", column_name) + ); + let result = + f64::from_str(&actual.out).map_err(|_| String::from("Failed to parse float."))?; + assert_eq!(result, expected_value); + } + Ok(()) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sum_of_a_row_containing_a_table_is_an_error() { + let actual = nu!( + cwd: "tests/fixtures/formats/", + "open sample-sys-output.json | math sum" + ); + assert!(actual + .err + .contains("Attempted to compute values that can't be operated on")); +} diff --git a/crates/nu-command/tests/commands/merge.rs b/crates/nu-command/tests/commands/merge.rs new file mode 100644 index 0000000000..384a1d1c8d --- /dev/null +++ b/crates/nu-command/tests/commands/merge.rs @@ -0,0 +1,44 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn row() { + Playground::setup("merge_test_1", |dirs, sandbox| { + sandbox.with_files(vec![ + FileWithContentToBeTrimmed( + "caballeros.csv", + r#" + name,country,luck + Andrés,Ecuador,0 + Jonathan,USA,0 + Jason,Canada,0 + Yehuda,USA,0 + "#, + ), + FileWithContentToBeTrimmed( + "new_caballeros.csv", + r#" + name,country,luck + Andrés Robalino,Guayaquil Ecuador,1 + Jonathan Turner,New Zealand,1 + "#, + ), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open caballeros.csv + | merge { open new_caballeros.csv } + | where country in ["Guayaquil Ecuador" "New Zealand"] + | get luck + | math sum + "# + )); + + assert_eq!(actual.out, "2"); + }) +} diff --git a/crates/nu-command/tests/commands/mkdir.rs b/crates/nu-command/tests/commands/mkdir.rs new file mode 100644 index 0000000000..ddb3b08560 --- /dev/null +++ b/crates/nu-command/tests/commands/mkdir.rs @@ -0,0 +1,84 @@ +use nu_test_support::fs::files_exist_at; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; +use std::path::Path; + +#[test] +fn creates_directory() { + Playground::setup("mkdir_test_1", |dirs, _| { + nu!( + cwd: dirs.test(), + "mkdir my_new_directory" + ); + + let expected = dirs.test().join("my_new_directory"); + + assert!(expected.exists()); + }) +} + +#[test] +fn accepts_and_creates_directories() { + Playground::setup("mkdir_test_2", |dirs, _| { + nu!( + cwd: dirs.test(), + "mkdir dir_1 dir_2 dir_3" + ); + + assert!(files_exist_at( + vec![Path::new("dir_1"), Path::new("dir_2"), Path::new("dir_3")], + dirs.test() + )); + }) +} + +#[test] +fn creates_intermediary_directories() { + Playground::setup("mkdir_test_3", |dirs, _| { + nu!( + cwd: dirs.test(), + "mkdir some_folder/another/deeper_one" + ); + + let expected = dirs.test().join("some_folder/another/deeper_one"); + + assert!(expected.exists()); + }) +} + +#[test] +fn create_directory_two_parents_up_using_multiple_dots() { + Playground::setup("mkdir_test_4", |dirs, sandbox| { + sandbox.within("foo").mkdir("bar"); + + nu!( + cwd: dirs.test().join("foo/bar"), + "mkdir .../boo" + ); + + let expected = dirs.test().join("boo"); + + assert!(expected.exists()); + }) +} + +#[test] +fn show_created_paths() { + Playground::setup("mkdir_test_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + pipeline( + r#" + mkdir -s dir_1 dir_2 dir_3 + | length + "# + )); + + assert!(files_exist_at( + vec![Path::new("dir_1"), Path::new("dir_2"), Path::new("dir_3")], + dirs.test() + )); + + assert_eq!(actual.out, "3"); + }) +} diff --git a/crates/nu-command/tests/commands/mod.rs b/crates/nu-command/tests/commands/mod.rs new file mode 100644 index 0000000000..f557aaee5a --- /dev/null +++ b/crates/nu-command/tests/commands/mod.rs @@ -0,0 +1,66 @@ +mod all; +mod any; +mod append; +mod cal; +mod cd; +mod compact; +mod cp; +mod def; +mod default; +mod drop; +mod each; +mod echo; +mod empty; +mod enter; +mod every; +mod find; +mod first; +mod flatten; +mod format; +mod get; +mod group_by; +mod hash_; +mod headers; +mod help; +mod histogram; +mod into_filesize; +mod into_int; +mod keep; +mod last; +mod length; +mod lines; +mod ls; +mod math; +mod merge; +mod mkdir; +mod move_; +mod open; +mod parse; +mod path; +mod prepend; +mod random; +mod range; +mod reduce; +mod rename; +mod reverse; +mod rm; +mod roll; +mod rotate; +mod save; +mod select; +mod semicolon; +mod skip; +mod sort_by; +mod source; +mod split_by; +mod split_column; +mod split_row; +mod str_; +mod touch; +mod uniq; +mod update; +mod where_; +mod which; +mod with_env; +mod wrap; +mod zip; diff --git a/crates/nu-command/tests/commands/move_/column.rs b/crates/nu-command/tests/commands/move_/column.rs new file mode 100644 index 0000000000..610b0390f3 --- /dev/null +++ b/crates/nu-command/tests/commands/move_/column.rs @@ -0,0 +1,143 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn moves_a_column_before() { + Playground::setup("move_column_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.csv", + r#" + column1,column2,column3,...,column98,column99,column100 + -------,-------,-------,---,--------, A ,--------- + -------,-------,-------,---,--------, N ,--------- + -------,-------,-------,---,--------, D ,--------- + -------,-------,-------,---,--------, R ,--------- + -------,-------,-------,---,--------, E ,--------- + -------,-------,-------,---,--------, S ,--------- + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.csv + | move column99 --before column1 + | rename chars + | get chars + | str trim + | str collect + "# + )); + + assert!(actual.out.contains("ANDRES")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn moves_columns_before() { + Playground::setup("move_column_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.csv", + r#" + column1,column2,column3,...,column98,column99,column100 + -------,-------, A ,---,--------, N ,--------- + -------,-------, D ,---,--------, R ,--------- + -------,-------, E ,---,--------, S ,--------- + -------,-------, : ,---,--------, : ,--------- + -------,-------, J ,---,--------, O ,--------- + -------,-------, N ,---,--------, A ,--------- + -------,-------, T ,---,--------, H ,--------- + -------,-------, A ,---,--------, N ,--------- + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.csv + | move column99 column3 --before column2 + | rename _ chars_1 chars_2 + | get chars_2 chars_1 + | str trim + | str collect + "# + )); + + assert!(actual.out.contains("ANDRES::JONATHAN")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn moves_a_column_after() { + Playground::setup("move_column_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.csv", + r#" + column1,column2,letters,...,column98,and_more,column100 + -------,-------, A ,---,--------, N ,--------- + -------,-------, D ,---,--------, R ,--------- + -------,-------, E ,---,--------, S ,--------- + -------,-------, : ,---,--------, : ,--------- + -------,-------, J ,---,--------, O ,--------- + -------,-------, N ,---,--------, A ,--------- + -------,-------, T ,---,--------, H ,--------- + -------,-------, A ,---,--------, N ,--------- + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.csv + | move letters --after and_more + | move letters and_more --before column2 + | rename _ chars_1 chars_2 + | get chars_1 chars_2 + | str trim + | str collect + "# + )); + + assert!(actual.out.contains("ANDRES::JONATHAN")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn moves_columns_after() { + Playground::setup("move_column_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.csv", + r#" + column1,column2,letters,...,column98,and_more,column100 + -------,-------, A ,---,--------, N ,--------- + -------,-------, D ,---,--------, R ,--------- + -------,-------, E ,---,--------, S ,--------- + -------,-------, : ,---,--------, : ,--------- + -------,-------, J ,---,--------, O ,--------- + -------,-------, N ,---,--------, A ,--------- + -------,-------, T ,---,--------, H ,--------- + -------,-------, A ,---,--------, N ,--------- + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.csv + | move letters and_more --after column1 + | get + | nth 1 2 + | str collect + "# + )); + + assert!(actual.out.contains("lettersand_more")); + }) +} diff --git a/crates/nu-command/tests/commands/move_/mod.rs b/crates/nu-command/tests/commands/move_/mod.rs new file mode 100644 index 0000000000..58d0a7f6cd --- /dev/null +++ b/crates/nu-command/tests/commands/move_/mod.rs @@ -0,0 +1,2 @@ +mod column; +mod mv; diff --git a/crates/nu-command/tests/commands/move_/mv.rs b/crates/nu-command/tests/commands/move_/mv.rs new file mode 100644 index 0000000000..1050a5a331 --- /dev/null +++ b/crates/nu-command/tests/commands/move_/mv.rs @@ -0,0 +1,371 @@ +use nu_test_support::fs::{files_exist_at, Stub::EmptyFile}; +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +#[test] +fn moves_a_file() { + Playground::setup("mv_test_1", |dirs, sandbox| { + sandbox + .with_files(vec![EmptyFile("andres.txt")]) + .mkdir("expected"); + + let original = dirs.test().join("andres.txt"); + let expected = dirs.test().join("expected/yehuda.txt"); + + nu!( + cwd: dirs.test(), + "mv andres.txt expected/yehuda.txt" + ); + + assert!(!original.exists()); + assert!(expected.exists()); + }) +} + +#[test] +fn overwrites_if_moving_to_existing_file() { + Playground::setup("mv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("andres.txt"), EmptyFile("jonathan.txt")]); + + let original = dirs.test().join("andres.txt"); + let expected = dirs.test().join("jonathan.txt"); + + nu!( + cwd: dirs.test(), + "mv andres.txt jonathan.txt" + ); + + assert!(!original.exists()); + assert!(expected.exists()); + }) +} + +#[test] +fn moves_a_directory() { + Playground::setup("mv_test_3", |dirs, sandbox| { + sandbox.mkdir("empty_dir"); + + let original_dir = dirs.test().join("empty_dir"); + let expected = dirs.test().join("renamed_dir"); + + nu!( + cwd: dirs.test(), + "mv empty_dir renamed_dir" + ); + + assert!(!original_dir.exists()); + assert!(expected.exists()); + }) +} + +#[test] +fn moves_the_file_inside_directory_if_path_to_move_is_existing_directory() { + Playground::setup("mv_test_4", |dirs, sandbox| { + sandbox + .with_files(vec![EmptyFile("jonathan.txt")]) + .mkdir("expected"); + + let original_dir = dirs.test().join("jonathan.txt"); + let expected = dirs.test().join("expected/jonathan.txt"); + + nu!( + cwd: dirs.test(), + "mv jonathan.txt expected" + ); + + assert!(!original_dir.exists()); + assert!(expected.exists()); + }) +} + +#[test] +fn moves_the_directory_inside_directory_if_path_to_move_is_existing_directory() { + Playground::setup("mv_test_5", |dirs, sandbox| { + sandbox + .within("contributors") + .with_files(vec![EmptyFile("jonathan.txt")]) + .mkdir("expected"); + + let original_dir = dirs.test().join("contributors"); + let expected = dirs.test().join("expected/contributors"); + + nu!( + cwd: dirs.test(), + "mv contributors expected" + ); + + assert!(!original_dir.exists()); + assert!(expected.exists()); + assert!(files_exist_at(vec!["jonathan.txt"], expected)) + }) +} + +#[test] +fn moves_using_path_with_wildcard() { + Playground::setup("mv_test_7", |dirs, sandbox| { + sandbox + .within("originals") + .with_files(vec![ + EmptyFile("andres.ini"), + EmptyFile("caco3_plastics.csv"), + EmptyFile("cargo_sample.toml"), + EmptyFile("jonathan.ini"), + EmptyFile("jonathan.xml"), + EmptyFile("sgml_description.json"), + EmptyFile("sample.ini"), + EmptyFile("utf16.ini"), + EmptyFile("yehuda.ini"), + ]) + .mkdir("work_dir") + .mkdir("expected"); + + let work_dir = dirs.test().join("work_dir"); + let expected = dirs.test().join("expected"); + + nu!(cwd: work_dir, "mv ../originals/*.ini ../expected"); + + assert!(files_exist_at( + vec!["yehuda.ini", "jonathan.ini", "sample.ini", "andres.ini",], + expected + )); + }) +} + +#[test] +fn moves_using_a_glob() { + Playground::setup("mv_test_8", |dirs, sandbox| { + sandbox + .within("meals") + .with_files(vec![ + EmptyFile("arepa.txt"), + EmptyFile("empanada.txt"), + EmptyFile("taquiza.txt"), + ]) + .mkdir("work_dir") + .mkdir("expected"); + + let meal_dir = dirs.test().join("meals"); + let work_dir = dirs.test().join("work_dir"); + let expected = dirs.test().join("expected"); + + nu!(cwd: work_dir, "mv ../meals/* ../expected"); + + assert!(meal_dir.exists()); + assert!(files_exist_at( + vec!["arepa.txt", "empanada.txt", "taquiza.txt",], + expected + )); + }) +} + +#[test] +fn moves_a_directory_with_files() { + Playground::setup("mv_test_9", |dirs, sandbox| { + sandbox + .mkdir("vehicles/car") + .mkdir("vehicles/bicycle") + .with_files(vec![ + EmptyFile("vehicles/car/car1.txt"), + EmptyFile("vehicles/car/car2.txt"), + ]) + .with_files(vec![ + EmptyFile("vehicles/bicycle/bicycle1.txt"), + EmptyFile("vehicles/bicycle/bicycle2.txt"), + ]); + + let original_dir = dirs.test().join("vehicles"); + let expected_dir = dirs.test().join("expected"); + + nu!( + cwd: dirs.test(), + "mv vehicles expected" + ); + + assert!(!original_dir.exists()); + assert!(expected_dir.exists()); + assert!(files_exist_at( + vec![ + "car/car1.txt", + "car/car2.txt", + "bicycle/bicycle1.txt", + "bicycle/bicycle2.txt" + ], + expected_dir + )); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_source_doesnt_exist() { + Playground::setup("mv_test_10", |dirs, sandbox| { + sandbox.mkdir("test_folder"); + let actual = nu!( + cwd: dirs.test(), + "mv non-existing-file test_folder/" + ); + assert!(actual.err.contains("Invalid file or pattern")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_destination_doesnt_exist() { + Playground::setup("mv_test_10_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("empty.txt")]); + + let actual = nu!( + cwd: dirs.test(), + "mv empty.txt does/not/exist" + ); + + assert!(actual.err.contains("Destination directory does not exist")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_multiple_sources_but_destination_not_a_directory() { + Playground::setup("mv_test_10_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("file1.txt"), + EmptyFile("file2.txt"), + EmptyFile("file3.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), + "mv file?.txt not_a_dir" + ); + + assert!(actual + .err + .contains("Can only move multiple sources if destination is a directory")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_renaming_directory_to_an_existing_file() { + Playground::setup("mv_test_10_3", |dirs, sandbox| { + sandbox + .mkdir("mydir") + .with_files(vec![EmptyFile("empty.txt")]); + + let actual = nu!( + cwd: dirs.test(), + "mv mydir empty.txt" + ); + + assert!(actual.err.contains("Cannot rename a directory to a file")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_moving_to_itself() { + Playground::setup("mv_test_10_4", |dirs, sandbox| { + sandbox.mkdir("mydir").mkdir("mydir/mydir_2"); + + let actual = nu!( + cwd: dirs.test(), + "mv mydir mydir/mydir_2/" + ); + + assert!(actual.err.contains("cannot move to itself")); + }) +} + +#[test] +fn does_not_error_on_relative_parent_path() { + Playground::setup("mv_test_11", |dirs, sandbox| { + sandbox + .mkdir("first") + .with_files(vec![EmptyFile("first/william_hartnell.txt")]); + + let original = dirs.test().join("first/william_hartnell.txt"); + let expected = dirs.test().join("william_hartnell.txt"); + + nu!( + cwd: dirs.test().join("first"), + "mv william_hartnell.txt ./.." + ); + + assert!(!original.exists()); + assert!(expected.exists()); + }) +} + +#[test] +fn move_files_using_glob_two_parents_up_using_multiple_dots() { + Playground::setup("mv_test_12", |dirs, sandbox| { + sandbox.within("foo").within("bar").with_files(vec![ + EmptyFile("jonathan.json"), + EmptyFile("andres.xml"), + EmptyFile("yehuda.yaml"), + EmptyFile("kevin.txt"), + EmptyFile("many_more.ppl"), + ]); + + nu!( + cwd: dirs.test().join("foo/bar"), + r#" + mv * ... + "# + ); + + let files = vec![ + "yehuda.yaml", + "jonathan.json", + "andres.xml", + "kevin.txt", + "many_more.ppl", + ]; + + let original_dir = dirs.test().join("foo/bar"); + let destination_dir = dirs.test(); + + assert!(files_exist_at(files.clone(), destination_dir)); + assert!(!files_exist_at(files, original_dir)) + }) +} + +#[test] +fn move_file_from_two_parents_up_using_multiple_dots_to_current_dir() { + Playground::setup("cp_test_10", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("hello_there")]); + sandbox.within("foo").mkdir("bar"); + + nu!( + cwd: dirs.test().join("foo/bar"), + r#" + mv .../hello_there . + "# + ); + + let expected = dirs.test().join("foo/bar/hello_there"); + let original = dirs.test().join("hello_there"); + + assert!(expected.exists()); + assert!(!original.exists()); + }) +} + +#[test] +fn does_not_error_when_some_file_is_moving_into_itself() { + Playground::setup("mv_test_13", |dirs, sandbox| { + sandbox.mkdir("11").mkdir("12"); + + let original_dir = dirs.test().join("11"); + let expected = dirs.test().join("12/11"); + nu!(cwd: dirs.test(), "mv 1* 12"); + + assert!(!original_dir.exists()); + assert!(expected.exists()); + }) +} diff --git a/crates/nu-command/tests/commands/nth.rs b/crates/nu-command/tests/commands/nth.rs new file mode 100644 index 0000000000..a65dcd7ed0 --- /dev/null +++ b/crates/nu-command/tests/commands/nth.rs @@ -0,0 +1,37 @@ +#[test] +fn selects_a_row() { + Playground::setup("nth_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("notes.txt"), EmptyFile("arepas.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | sort-by name + | nth 0 + | get name + "# + )); + + assert_eq!(actual.out, "arepas.txt"); + }); +} + +#[test] +fn selects_many_rows() { + Playground::setup("nth_test_2", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("notes.txt"), EmptyFile("arepas.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | nth 1 0 + | length + "# + )); + + assert_eq!(actual.out, "2"); + }); +} diff --git a/crates/nu-command/tests/commands/open.rs b/crates/nu-command/tests/commands/open.rs new file mode 100644 index 0000000000..ea11ffd01e --- /dev/null +++ b/crates/nu-command/tests/commands/open.rs @@ -0,0 +1,245 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn parses_csv() { + Playground::setup("open_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "nu.zion.csv", + r#" + author,lang,source + Jonathan Turner,Rust,New Zealand + Andres N. Robalino,Rust,Ecuador + Yehuda Katz,Rust,Estados Unidos + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nu.zion.csv + | where author == "Andres N. Robalino" + | get source + "# + )); + + assert_eq!(actual.out, "Ecuador"); + }) +} + +// sample.bson has the following format: +// ━━━━━━━━━━┯━━━━━━━━━━━ +// _id │ root +// ──────────┼─────────── +// [object] │ [9 items] +// ━━━━━━━━━━┷━━━━━━━━━━━ +// +// the root value is: +// ━━━┯━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━┯━━━━━━━━━━┯━━━━━━━━━━ +// # │ _id │ a │ b │ c +// ───┼───────────────────┼─────────────────────────┼──────────┼────────── +// 0 │ [object] │ 1.000000000000000 │ hello │ [2 items] +// 1 │ [object] │ 42.00000000000000 │ whel │ hello +// 2 │ [object] │ [object] │ │ +// 3 │ [object] │ │ [object] │ +// 4 │ [object] │ │ │ [object] +// 5 │ [object] │ │ │ [object] +// 6 │ [object] │ [object] │ [object] │ +// 7 │ [object] │ │ [object] │ +// 8 │ 1.000000 │ │ [object] │ +// +// The decimal value is supposed to be π, but is currently wrong due to +// what appears to be an issue in the bson library that is under investigation. +// + +#[cfg(feature = "bson")] +#[test] +fn parses_bson() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open sample.bson | get root | nth 0 | get b" + ); + + assert_eq!(actual.out, "hello"); +} + +#[cfg(feature = "bson")] +#[test] +fn parses_more_bson_complexity() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.bson + | get root + | nth 6 + | get b + | get '$binary_subtype' + "# + )); + + assert_eq!(actual.out, "function"); +} + +// sample.db has the following format: +// +// ━━━┯━━━━━━━━━━━━┯━━━━━━━━━━━━━━ +// # │ table_name │ table_values +// ───┼────────────┼────────────── +// 0 │ strings │ [6 items] +// 1 │ ints │ [5 items] +// 2 │ floats │ [4 items] +// ━━━┷━━━━━━━━━━━━┷━━━━━━━━━━━━━━ +// +// In this case, this represents a sqlite database +// with three tables named `strings`, `ints`, and `floats`. +// The table_values represent the values for the tables: +// +// ━━━━┯━━━━━━━┯━━━━━━━━━━┯━━━━━━┯━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// # │ x │ y │ z │ f +// ────┼───────┼──────────┼──────┼────────────────────────────────────────────────────────────────────── +// 0 │ hello │ │ │ +// 1 │ hello │ │ │ +// 2 │ hello │ │ │ +// 3 │ hello │ │ │ +// 4 │ world │ │ │ +// 5 │ world │ │ │ +// 6 │ │ │ 1 │ +// 7 │ │ │ 42 │ +// 8 │ │ │ 425 │ +// 9 │ │ │ 4253 │ +// 10 │ │ │ │ +// 11 │ │ │ │ 3.400000000000000 +// 12 │ │ │ │ 3.141592650000000 +// 13 │ │ │ │ 23.00000000000000 +// 14 │ │ │ │ this string that doesn't really belong here but sqlite is what it is +// ━━━━┷━━━━━━━┷━━━━━━━━━━┷━━━━━━┷━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ +// +// We can see here that each table has different columns. `strings` has `x` and `y`, while +// `ints` has just `z`, and `floats` has only the column `f`. This means, in general, when working +// with sqlite, one will want to select a single table, e.g.: +// +// open sample.db | nth 1 | get table_values +// ━━━┯━━━━━━ +// # │ z +// ───┼────── +// 0 │ 1 +// 1 │ 42 +// 2 │ 425 +// 3 │ 4253 +// 4 │ +// ━━━┷━━━━━━ + +#[cfg(feature = "sqlite")] +#[test] +fn parses_sqlite() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | get table_values + | nth 2 + | get x + "# + )); + + assert_eq!(actual.out, "hello"); +} + +#[test] +fn parses_toml() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open cargo_sample.toml | get package.edition" + ); + + assert_eq!(actual.out, "2018"); +} + +#[test] +fn parses_tsv() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open caco3_plastics.tsv + | first 1 + | get origin + "# + )); + + assert_eq!(actual.out, "SPAIN") +} + +#[test] +fn parses_json() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sgml_description.json + | get glossary.GlossDiv.GlossList.GlossEntry.GlossSee + "# + )); + + assert_eq!(actual.out, "markup") +} + +#[test] +fn parses_ini() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open sample.ini | get SectionOne.integer" + ); + + assert_eq!(actual.out, "1234") +} + +#[test] +fn parses_utf16_ini() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open ./utf16.ini --raw | decode utf-16 | from ini | rename info | get info | get IconIndex" + ); + + assert_eq!(actual.out, "-236") +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_file_not_found() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open i_dont_exist.txt" + ); + let expected = "Cannot find file"; + assert!( + actual.err.contains(expected), + "Error:\n{}\ndoes not contain{}", + actual.err, + expected + ); +} + +// FIXME: jt: I think `open` on a directory is confusing. We should make discuss this one a bit more +#[ignore] +#[test] +fn open_dir_is_ls() { + Playground::setup("open_dir", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open . + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} diff --git a/crates/nu-command/tests/commands/parse.rs b/crates/nu-command/tests/commands/parse.rs new file mode 100644 index 0000000000..40d7fc917f --- /dev/null +++ b/crates/nu-command/tests/commands/parse.rs @@ -0,0 +1,191 @@ +use nu_test_support::fs::Stub; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +mod simple { + use super::*; + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn extracts_fields_from_the_given_the_pattern() { + Playground::setup("parse_test_1", |dirs, sandbox| { + sandbox.with_files(vec![Stub::FileWithContentToBeTrimmed( + "key_value_separated_arepa_ingredients.txt", + r#" + VAR1=Cheese + VAR2=JonathanParsed + VAR3=NushellSecretIngredient + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open key_value_separated_arepa_ingredients.txt + | lines + | each { echo $it | parse "{Name}={Value}" } + | nth 1 + | get Value + "# + )); + + assert_eq!(actual.out, "JonathanParsed"); + }) + } + + #[test] + fn double_open_curly_evalutes_to_a_single_curly() { + Playground::setup("parse_test_regex_2", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "{abc}123" + | parse "{{abc}{name}" + | get name + "# + )); + + assert_eq!(actual.out, "123"); + }) + } + + #[test] + fn properly_escapes_text() { + Playground::setup("parse_test_regex_3", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "(abc)123" + | parse "(abc){name}" + | get name + "# + )); + + assert_eq!(actual.out, "123"); + }) + } + + #[test] + fn properly_captures_empty_column() { + Playground::setup("parse_test_regex_4", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo ["1:INFO:component:all is well" "2:ERROR::something bad happened"] + | parse "{timestamp}:{level}:{tag}:{entry}" + | get entry + | nth 1 + "# + )); + + assert_eq!(actual.out, "something bad happened"); + }) + } + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn errors_when_missing_closing_brace() { + Playground::setup("parse_test_regex_5", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "(abc)123" + | parse "(abc){name" + | get name + "# + )); + + assert!(actual.err.contains("invalid parse pattern")); + }) + } +} + +mod regex { + use super::*; + + fn nushell_git_log_oneline<'a>() -> Vec> { + vec![Stub::FileWithContentToBeTrimmed( + "nushell_git_log_oneline.txt", + r#" + ae87582c Fix missing invocation errors (#1846) + b89976da let format access variables also (#1842) + "#, + )] + } + + #[test] + fn extracts_fields_with_all_named_groups() { + Playground::setup("parse_test_regex_1", |dirs, sandbox| { + sandbox.with_files(nushell_git_log_oneline()); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nushell_git_log_oneline.txt + | parse --regex "(?P\w+) (?P.+) \(#(?P\d+)\)" + | nth 1 + | get PR + "# + )); + + assert_eq!(actual.out, "1842"); + }) + } + + #[test] + fn extracts_fields_with_all_unnamed_groups() { + Playground::setup("parse_test_regex_2", |dirs, sandbox| { + sandbox.with_files(nushell_git_log_oneline()); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nushell_git_log_oneline.txt + | parse --regex "(\w+) (.+) \(#(\d+)\)" + | nth 1 + | get Capture1 + "# + )); + + assert_eq!(actual.out, "b89976da"); + }) + } + + #[test] + fn extracts_fields_with_named_and_unnamed_groups() { + Playground::setup("parse_test_regex_3", |dirs, sandbox| { + sandbox.with_files(nushell_git_log_oneline()); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nushell_git_log_oneline.txt + | parse --regex "(?P\w+) (.+) \(#(?P\d+)\)" + | nth 1 + | get Capture2 + "# + )); + + assert_eq!(actual.out, "let format access variables also"); + }) + } + + #[test] + fn errors_with_invalid_regex() { + Playground::setup("parse_test_regex_1", |dirs, sandbox| { + sandbox.with_files(nushell_git_log_oneline()); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nushell_git_log_oneline.txt + | parse --regex "(?P\w+ unfinished capture group" + "# + )); + + assert!(actual.err.contains("unclosed group")); + }) + } +} diff --git a/crates/nu-command/tests/commands/path/basename.rs b/crates/nu-command/tests/commands/path/basename.rs new file mode 100644 index 0000000000..a16c8202c9 --- /dev/null +++ b/crates/nu-command/tests/commands/path/basename.rs @@ -0,0 +1,83 @@ +use nu_test_support::{nu, pipeline}; + +use super::join_path_sep; + +#[test] +fn returns_basename_of_empty_input() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "" + | path basename + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn replaces_basename_of_empty_input() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "" + | path basename -r newname.txt + "# + )); + + assert_eq!(actual.out, "newname.txt"); +} + +#[test] +fn returns_basename_of_path_ending_with_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/file.txt/." + | path basename + "# + )); + + assert_eq!(actual.out, "file.txt"); +} + +#[test] +fn replaces_basename_of_path_ending_with_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/file.txt/." + | path basename -r viking.txt + "# + )); + + let expected = join_path_sep(&["some", "viking.txt"]); + assert_eq!(actual.out, expected); +} + +#[test] +fn returns_basename_of_path_ending_with_double_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/file.txt/.." + | path basename + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn replaces_basename_of_path_ending_with_double_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/file.txt/.." + | path basename -r eggs + "# + )); + + let expected = join_path_sep(&["some/file.txt/..", "eggs"]); + assert_eq!(actual.out, expected); +} diff --git a/crates/nu-command/tests/commands/path/dirname.rs b/crates/nu-command/tests/commands/path/dirname.rs new file mode 100644 index 0000000000..a935c1fb34 --- /dev/null +++ b/crates/nu-command/tests/commands/path/dirname.rs @@ -0,0 +1,137 @@ +use nu_test_support::{nu, pipeline}; + +use super::join_path_sep; + +#[test] +fn returns_dirname_of_empty_input() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "" + | path dirname + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn replaces_dirname_of_empty_input() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "" + | path dirname -r newdir + "# + )); + + assert_eq!(actual.out, "newdir"); +} + +#[test] +fn returns_dirname_of_path_ending_with_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/." + | path dirname + "# + )); + + assert_eq!(actual.out, "some"); +} + +#[test] +fn replaces_dirname_of_path_ending_with_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/." + | path dirname -r eggs + "# + )); + + let expected = join_path_sep(&["eggs", "dir"]); + assert_eq!(actual.out, expected); +} + +#[test] +fn returns_dirname_of_path_ending_with_double_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/.." + | path dirname + "# + )); + + assert_eq!(actual.out, "some/dir"); +} + +#[test] +fn replaces_dirname_of_path_with_double_dot() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/.." + | path dirname -r eggs + "# + )); + + let expected = join_path_sep(&["eggs", ".."]); + assert_eq!(actual.out, expected); +} + +#[test] +fn returns_dirname_of_zero_levels() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/with/spam.txt" + | path dirname -n 0 + "# + )); + + assert_eq!(actual.out, "some/dir/with/spam.txt"); +} + +#[test] +fn replaces_dirname_of_zero_levels_with_empty_string() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/with/spam.txt" + | path dirname -n 0 -r "" + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn replaces_dirname_of_more_levels() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/with/spam.txt" + | path dirname -r eggs -n 2 + "# + )); + + let expected = join_path_sep(&["eggs", "with/spam.txt"]); + assert_eq!(actual.out, expected); +} + +#[test] +fn replaces_dirname_of_way_too_many_levels() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "some/dir/with/spam.txt" + | path dirname -r eggs -n 999 + "# + )); + + let expected = join_path_sep(&["eggs", "some/dir/with/spam.txt"]); + assert_eq!(actual.out, expected); +} diff --git a/crates/nu-command/tests/commands/path/exists.rs b/crates/nu-command/tests/commands/path/exists.rs new file mode 100644 index 0000000000..0ab51ac939 --- /dev/null +++ b/crates/nu-command/tests/commands/path/exists.rs @@ -0,0 +1,55 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn checks_if_existing_file_exists() { + Playground::setup("path_exists_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("spam.txt")]); + + let actual = nu!( + cwd: dirs.test(), + "echo spam.txt | path exists" + ); + + assert_eq!(actual.out, "true"); + }) +} + +#[test] +fn checks_if_missing_file_exists() { + Playground::setup("path_exists_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + "echo spam.txt | path exists" + ); + + assert_eq!(actual.out, "false"); + }) +} + +#[test] +fn checks_if_dot_exists() { + Playground::setup("path_exists_3", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + "echo '.' | path exists" + ); + + assert_eq!(actual.out, "true"); + }) +} + +#[test] +fn checks_if_double_dot_exists() { + Playground::setup("path_exists_4", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), + "echo '..' | path exists" + ); + + assert_eq!(actual.out, "true"); + }) +} diff --git a/crates/nu-command/tests/commands/path/expand.rs b/crates/nu-command/tests/commands/path/expand.rs new file mode 100644 index 0000000000..08ca9dc587 --- /dev/null +++ b/crates/nu-command/tests/commands/path/expand.rs @@ -0,0 +1,78 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +use std::path::PathBuf; + +#[test] +fn expands_path_with_dot() { + Playground::setup("path_expand_1", |dirs, sandbox| { + sandbox + .within("menu") + .with_files(vec![EmptyFile("spam.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "menu/./spam.txt" + | path expand + "# + )); + + let expected = dirs.test.join("menu").join("spam.txt"); + assert_eq!(PathBuf::from(actual.out), expected); + }) +} + +#[test] +fn expands_path_with_double_dot() { + Playground::setup("path_expand_2", |dirs, sandbox| { + sandbox + .within("menu") + .with_files(vec![EmptyFile("spam.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "menu/../menu/spam.txt" + | path expand + "# + )); + + let expected = dirs.test.join("menu").join("spam.txt"); + assert_eq!(PathBuf::from(actual.out), expected); + }) +} + +#[cfg(windows)] +mod windows { + use super::*; + + #[test] + fn expands_path_with_tilde_backward_slash() { + Playground::setup("path_expand_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "~\tmp.txt" | path expand + "# + )); + + assert!(!PathBuf::from(actual.out).starts_with("~")); + }) + } + + #[test] + fn win_expands_path_with_tilde_forward_slash() { + Playground::setup("path_expand_2", |dirs, _| { + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "~/tmp.txt" | path expand + "# + )); + + assert!(!PathBuf::from(actual.out).starts_with("~")); + }) + } +} diff --git a/crates/nu-command/tests/commands/path/join.rs b/crates/nu-command/tests/commands/path/join.rs new file mode 100644 index 0000000000..b7ffa73538 --- /dev/null +++ b/crates/nu-command/tests/commands/path/join.rs @@ -0,0 +1,59 @@ +use nu_test_support::{nu, pipeline}; + +use super::join_path_sep; + +#[test] +fn returns_path_joined_with_column_path() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo [ [name]; [eggs] ] + | path join spam.txt -c [ name ] + | get name + "# + )); + + let expected = join_path_sep(&["eggs", "spam.txt"]); + assert_eq!(actual.out, expected); +} + +#[test] +fn returns_path_joined_from_list() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo [ home viking spam.txt ] + | path join + "# + )); + + let expected = join_path_sep(&["home", "viking", "spam.txt"]); + assert_eq!(actual.out, expected); +} + +#[test] +fn appends_slash_when_joined_with_empty_path() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "/some/dir" + | path join '' + "# + )); + + let expected = join_path_sep(&["/some/dir", ""]); + assert_eq!(actual.out, expected); +} + +#[test] +fn returns_joined_path_when_joining_empty_path() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "" + | path join foo.txt + "# + )); + + assert_eq!(actual.out, "foo.txt"); +} diff --git a/crates/nu-command/tests/commands/path/mod.rs b/crates/nu-command/tests/commands/path/mod.rs new file mode 100644 index 0000000000..c836c5691d --- /dev/null +++ b/crates/nu-command/tests/commands/path/mod.rs @@ -0,0 +1,34 @@ +mod basename; +mod dirname; +mod exists; +mod expand; +mod join; +mod parse; +mod split; +mod type_; + +use std::path::MAIN_SEPARATOR; + +/// Helper function that joins string literals with '/' or '\', based on host OS +fn join_path_sep(pieces: &[&str]) -> String { + let sep_string = String::from(MAIN_SEPARATOR); + pieces.join(&sep_string) +} + +#[cfg(windows)] +#[test] +fn joins_path_on_windows() { + let pieces = ["sausage", "bacon", "spam"]; + let actual = join_path_sep(&pieces); + + assert_eq!(&actual, "sausage\\bacon\\spam"); +} + +#[cfg(not(windows))] +#[test] +fn joins_path_on_other_than_windows() { + let pieces = ["sausage", "bacon", "spam"]; + let actual = join_path_sep(&pieces); + + assert_eq!(&actual, "sausage/bacon/spam"); +} diff --git a/crates/nu-command/tests/commands/path/parse.rs b/crates/nu-command/tests/commands/path/parse.rs new file mode 100644 index 0000000000..4ca1bf1e02 --- /dev/null +++ b/crates/nu-command/tests/commands/path/parse.rs @@ -0,0 +1,138 @@ +use nu_test_support::{nu, pipeline}; + +#[cfg(windows)] +#[test] +fn parses_single_path_prefix() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'C:\users\viking\spam.txt' + | path parse + | get prefix + "# + )); + + assert_eq!(actual.out, "C:"); +} + +#[test] +fn parses_single_path_parent() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.txt' + | path parse + | get parent + "# + )); + + assert_eq!(actual.out, "home/viking"); +} + +#[test] +fn parses_single_path_stem() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.txt' + | path parse + | get stem + "# + )); + + assert_eq!(actual.out, "spam"); +} + +#[test] +fn parses_custom_extension_gets_extension() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.tar.gz' + | path parse -e tar.gz + | get extension + "# + )); + + assert_eq!(actual.out, "tar.gz"); +} + +#[test] +fn parses_custom_extension_gets_stem() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.tar.gz' + | path parse -e tar.gz + | get stem + "# + )); + + assert_eq!(actual.out, "spam"); +} + +#[test] +fn parses_ignoring_extension_gets_extension() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.tar.gz' + | path parse -e '' + | get extension + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn parses_ignoring_extension_gets_stem() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.tar.gz' + | path parse -e "" + | get stem + "# + )); + + assert_eq!(actual.out, "spam.tar.gz"); +} + +#[test] +fn parses_column_path_extension() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo [[home, barn]; ['home/viking/spam.txt', 'barn/cow/moo.png']] + | path parse -c [ home barn ] + | get barn + | get extension + "# + )); + + assert_eq!(actual.out, "png"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn parses_into_correct_number_of_columns() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo 'home/viking/spam.txt' + | path parse + | pivot + | get Column0 + | length + "# + )); + + #[cfg(windows)] + let expected = "4"; + #[cfg(not(windows))] + let expected = "3"; + + assert_eq!(actual.out, expected); +} diff --git a/crates/nu-command/tests/commands/path/split.rs b/crates/nu-command/tests/commands/path/split.rs new file mode 100644 index 0000000000..ffd51cd434 --- /dev/null +++ b/crates/nu-command/tests/commands/path/split.rs @@ -0,0 +1,48 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn splits_empty_path() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo '' | path split + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn splits_correctly_single_path() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + 'home/viking/spam.txt' + | path split + | last + "# + )); + + assert_eq!(actual.out, "spam.txt"); +} + +#[test] +fn splits_correctly_with_column_path() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo [ + [home, barn]; + + ['home/viking/spam.txt', 'barn/cow/moo.png'] + ['home/viking/eggs.txt', 'barn/goat/cheese.png'] + ] + | path split -c [ home barn ] + | get barn + | flatten + | length + "# + )); + + assert_eq!(actual.out, "6"); +} diff --git a/crates/nu-command/tests/commands/path/type_.rs b/crates/nu-command/tests/commands/path/type_.rs new file mode 100644 index 0000000000..43a599558f --- /dev/null +++ b/crates/nu-command/tests/commands/path/type_.rs @@ -0,0 +1,58 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn returns_type_of_missing_file() { + let actual = nu!( + cwd: "tests", pipeline( + r#" + echo "spam.txt" + | path type + "# + )); + + assert_eq!(actual.out, ""); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn returns_type_of_existing_file() { + Playground::setup("path_expand_1", |dirs, sandbox| { + sandbox + .within("menu") + .with_files(vec![EmptyFile("spam.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "menu" + | path type + "# + )); + + assert_eq!(actual.out, "dir"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn returns_type_of_existing_directory() { + Playground::setup("path_expand_1", |dirs, sandbox| { + sandbox + .within("menu") + .with_files(vec![EmptyFile("spam.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + echo "menu/spam.txt" + | path type + "# + )); + + assert_eq!(actual.out, "file"); + }) +} diff --git a/crates/nu-command/tests/commands/prepend.rs b/crates/nu-command/tests/commands/prepend.rs new file mode 100644 index 0000000000..1c872becba --- /dev/null +++ b/crates/nu-command/tests/commands/prepend.rs @@ -0,0 +1,29 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn adds_a_row_to_the_beginning() { + Playground::setup("prepend_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.txt", + r#" + Andrés N. Robalino + Jonathan Turner + Yehuda Katz + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.txt + | lines + | prepend "pollo loco" + | nth 0 + "# + )); + + assert_eq!(actual.out, "pollo loco"); + }) +} diff --git a/crates/nu-command/tests/commands/random/bool.rs b/crates/nu-command/tests/commands/random/bool.rs new file mode 100644 index 0000000000..862ba2840c --- /dev/null +++ b/crates/nu-command/tests/commands/random/bool.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn generates_a_bool() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random bool + "# + )); + + let output = actual.out; + let is_boolean_output = output == "true" || output == "false"; + + assert!(is_boolean_output); +} diff --git a/crates/nu-command/tests/commands/random/chars.rs b/crates/nu-command/tests/commands/random/chars.rs new file mode 100644 index 0000000000..c77f347364 --- /dev/null +++ b/crates/nu-command/tests/commands/random/chars.rs @@ -0,0 +1,14 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn generates_chars_of_specified_length() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random chars -l 15 | size | get chars + "# + )); + + let result = actual.out; + assert_eq!(result, "15"); +} diff --git a/crates/nu-command/tests/commands/random/decimal.rs b/crates/nu-command/tests/commands/random/decimal.rs new file mode 100644 index 0000000000..74c064d10e --- /dev/null +++ b/crates/nu-command/tests/commands/random/decimal.rs @@ -0,0 +1,43 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn generates_an_decimal() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random decimal 42..43 + "# + )); + + assert!(actual.out.contains("42") || actual.out.contains("43")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn generates_55() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random decimal 55..55 + "# + )); + + assert!(actual.out.contains("55")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn generates_0() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random decimal ..<1 + "# + )); + + assert!(actual.out.contains('0')); +} diff --git a/crates/nu-command/tests/commands/random/dice.rs b/crates/nu-command/tests/commands/random/dice.rs new file mode 100644 index 0000000000..a056f07c9d --- /dev/null +++ b/crates/nu-command/tests/commands/random/dice.rs @@ -0,0 +1,13 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn rolls_4_roll() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random dice -d 4 -s 10 | length + "# + )); + + assert_eq!(actual.out, "4"); +} diff --git a/crates/nu-command/tests/commands/random/integer.rs b/crates/nu-command/tests/commands/random/integer.rs new file mode 100644 index 0000000000..9ca86ff261 --- /dev/null +++ b/crates/nu-command/tests/commands/random/integer.rs @@ -0,0 +1,39 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn generates_an_integer() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random integer 42..43 + "# + )); + + assert!(actual.out.contains("42") || actual.out.contains("43")); +} + +#[test] +fn generates_55() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random integer 55..55 + "# + )); + + assert!(actual.out.contains("55")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn generates_0() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random integer ..<1 + "# + )); + + assert!(actual.out.contains('0')); +} diff --git a/crates/nu-command/tests/commands/random/mod.rs b/crates/nu-command/tests/commands/random/mod.rs new file mode 100644 index 0000000000..9b3bf6eb88 --- /dev/null +++ b/crates/nu-command/tests/commands/random/mod.rs @@ -0,0 +1,7 @@ +mod bool; +mod chars; +mod decimal; +mod dice; +mod integer; +#[cfg(feature = "uuid_crate")] +mod uuid; diff --git a/crates/nu-command/tests/commands/random/uuid.rs b/crates/nu-command/tests/commands/random/uuid.rs new file mode 100644 index 0000000000..725081383c --- /dev/null +++ b/crates/nu-command/tests/commands/random/uuid.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; +use uuid_crate::Uuid; + +#[test] +fn generates_valid_uuid4() { + let actual = nu!( + cwd: ".", pipeline( + r#" + random uuid + "# + )); + + let result = Uuid::parse_str(actual.out.as_str()); + + assert!(result.is_ok()); +} diff --git a/crates/nu-command/tests/commands/range.rs b/crates/nu-command/tests/commands/range.rs new file mode 100644 index 0000000000..7aa3e4e66b --- /dev/null +++ b/crates/nu-command/tests/commands/range.rs @@ -0,0 +1,68 @@ +use nu_test_support::fs::Stub::EmptyFile; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn selects_a_row() { + Playground::setup("range_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("notes.txt"), EmptyFile("tests.txt")]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | sort-by name + | range 0..0 + | get name + "# + )); + + assert_eq!(actual.out, "notes.txt"); + }); +} + +#[test] +fn selects_some_rows() { + Playground::setup("range_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("notes.txt"), + EmptyFile("tests.txt"), + EmptyFile("persons.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | range 1..2 + | length + "# + )); + + assert_eq!(actual.out, "2"); + }); +} + +#[test] +fn negative_indices() { + Playground::setup("range_test_negative_indices", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("notes.txt"), + EmptyFile("tests.txt"), + EmptyFile("persons.txt"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | range (-1..) + | length + "# + )); + + assert_eq!(actual.out, "1"); + }); +} diff --git a/crates/nu-command/tests/commands/reduce.rs b/crates/nu-command/tests/commands/reduce.rs new file mode 100644 index 0000000000..b5584f979d --- /dev/null +++ b/crates/nu-command/tests/commands/reduce.rs @@ -0,0 +1,138 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn reduce_table_column() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "[{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}]" + | from json + | get total + | reduce -f 20 { $it.item + (math eval $"($item.acc)^1.05")} + | into string -d 1 + "# + ) + ); + + assert_eq!(actual.out, "180.6"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn reduce_table_column_with_path() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [{month:2,total:30}, {month:3,total:10}, {month:4,total:3}, {month:5,total:60}] + | reduce -f 20 { $it.item.total + (math eval $"($item.acc)^1.05")} + | into string -d 1 + "# + ) + ); + + assert_eq!(actual.out, "180.6"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn reduce_rows_example() { + let actual = nu!( + cwd: ".", pipeline( + r#" + [[a,b]; [1,2] [3,4]] + | reduce -f 1.6 { $it.acc * ($it.item.a | into int) + ($it.item.b | into int) } + "# + ) + ); + + assert_eq!(actual.out, "14.8"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn reduce_numbered_example() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo one longest three bar + | reduce -n { if ($it.item.item | str length) > ($it.acc.item | str length) {echo $it.item} else {echo $it.acc}} + | get index + "# + ) + ); + + assert_eq!(actual.out, "1"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn reduce_numbered_integer_addition_example() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [1 2 3 4] + | reduce -n { $it.acc.item + $it.item.item } + | get item + "# + ) + ); + + assert_eq!(actual.out, "10"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn folding_with_tables() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [10 20 30 40] + | reduce -f [] { + with-env [value $it.item] { + echo $acc | append (10 * ($env.value | into int)) + } + } + | math sum + "# + ) + ); + + assert_eq!(actual.out, "1000"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn error_reduce_fold_type_mismatch() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo a b c | reduce -f 0 { $it.acc + $it.item } + "# + ) + ); + + assert!(actual.err.contains("mismatch")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn error_reduce_empty() { + let actual = nu!( + cwd: ".", pipeline( + r#" + reduce { $it.$acc + $it.item } + "# + ) + ); + + assert!(actual.err.contains("needs input")); +} diff --git a/crates/nu-command/tests/commands/rename.rs b/crates/nu-command/tests/commands/rename.rs new file mode 100644 index 0000000000..1d5b35d479 --- /dev/null +++ b/crates/nu-command/tests/commands/rename.rs @@ -0,0 +1,91 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn changes_the_column_name() { + Playground::setup("rename_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_cuatro_mosqueteros.txt", + r#" + Andrés N. Robalino + Jonathan Turner + Yehuda Katz + Jason Gedge + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_cuatro_mosqueteros.txt + | lines + | wrap name + | rename mosqueteros + | get mosqueteros + | length + "# + )); + + assert_eq!(actual.out, "4"); + }) +} + +#[test] +fn keeps_remaining_original_names_given_less_new_names_than_total_original_names() { + Playground::setup("rename_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_cuatro_mosqueteros.txt", + r#" + Andrés N. Robalino + Jonathan Turner + Yehuda Katz + Jason Gedge + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_cuatro_mosqueteros.txt + | lines + | wrap name + | default hit "arepa!" + | rename mosqueteros + | get hit + | length + "# + )); + + assert_eq!(actual.out, "4"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_no_columns_present() { + Playground::setup("rename_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_cuatro_mosqueteros.txt", + r#" + Andrés N. Robalino + Jonathan Turner + Yehuda Katz + Jason Gedge + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_cuatro_mosqueteros.txt + | lines + | rename mosqueteros + "# + )); + + assert!(actual.err.contains("no column names available")); + assert!(actual.err.contains("can't rename")); + }) +} diff --git a/crates/nu-command/tests/commands/reverse.rs b/crates/nu-command/tests/commands/reverse.rs new file mode 100644 index 0000000000..e994aa4927 --- /dev/null +++ b/crates/nu-command/tests/commands/reverse.rs @@ -0,0 +1,11 @@ +use nu_test_support::nu; + +#[test] +fn can_get_reverse_first() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "ls | sort-by name | reverse | first 1 | get name | str trim " + ); + + assert_eq!(actual.out, "utf16.ini"); +} diff --git a/crates/nu-command/tests/commands/rm.rs b/crates/nu-command/tests/commands/rm.rs new file mode 100644 index 0000000000..ddbe7180ca --- /dev/null +++ b/crates/nu-command/tests/commands/rm.rs @@ -0,0 +1,334 @@ +use nu_test_support::fs::{files_exist_at, Stub::EmptyFile}; +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +#[test] +fn removes_a_file() { + Playground::setup("rm_test_1", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("i_will_be_deleted.txt")]); + + nu!( + cwd: dirs.root(), + "rm rm_test_1/i_will_be_deleted.txt" + ); + + let path = dirs.test().join("i_will_be_deleted.txt"); + + assert!(!path.exists()); + }) +} + +#[test] +fn removes_files_with_wildcard() { + Playground::setup("rm_test_2", |dirs, sandbox| { + sandbox + .within("src") + .with_files(vec![ + EmptyFile("cli.rs"), + EmptyFile("lib.rs"), + EmptyFile("prelude.rs"), + ]) + .within("src/parser") + .with_files(vec![EmptyFile("parse.rs"), EmptyFile("parser.rs")]) + .within("src/parser/parse") + .with_files(vec![EmptyFile("token_tree.rs")]) + .within("src/parser/hir") + .with_files(vec![ + EmptyFile("baseline_parse.rs"), + EmptyFile("baseline_parse_tokens.rs"), + ]); + + nu!( + cwd: dirs.test(), + r#"rm "src/*/*/*.rs""# + ); + + assert!(!files_exist_at( + vec![ + "src/parser/parse/token_tree.rs", + "src/parser/hir/baseline_parse.rs", + "src/parser/hir/baseline_parse_tokens.rs" + ], + dirs.test() + )); + + assert_eq!( + Playground::glob_vec(&format!("{}/src/*/*/*.rs", dirs.test().display())), + Vec::::new() + ); + }) +} + +#[test] +fn removes_deeply_nested_directories_with_wildcard_and_recursive_flag() { + Playground::setup("rm_test_3", |dirs, sandbox| { + sandbox + .within("src") + .with_files(vec![ + EmptyFile("cli.rs"), + EmptyFile("lib.rs"), + EmptyFile("prelude.rs"), + ]) + .within("src/parser") + .with_files(vec![EmptyFile("parse.rs"), EmptyFile("parser.rs")]) + .within("src/parser/parse") + .with_files(vec![EmptyFile("token_tree.rs")]) + .within("src/parser/hir") + .with_files(vec![ + EmptyFile("baseline_parse.rs"), + EmptyFile("baseline_parse_tokens.rs"), + ]); + + nu!( + cwd: dirs.test(), + "rm -r src/*" + ); + + assert!(!files_exist_at( + vec!["src/parser/parse", "src/parser/hir"], + dirs.test() + )); + }) +} + +#[test] +fn removes_directory_contents_without_recursive_flag_if_empty() { + Playground::setup("rm_test_4", |dirs, _| { + nu!( + cwd: dirs.root(), + "rm rm_test_4" + ); + + assert!(!dirs.test().exists()); + }) +} + +#[test] +fn removes_directory_contents_with_recursive_flag() { + Playground::setup("rm_test_5", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]); + + nu!( + cwd: dirs.root(), + "rm rm_test_5 --recursive" + ); + + assert!(!dirs.test().exists()); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_attempting_to_delete_a_directory_with_content_without_recursive_flag() { + Playground::setup("rm_test_6", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("some_empty_file.txt")]); + let actual = nu!( + cwd: dirs.root(), + "rm rm_test_6" + ); + + assert!(dirs.test().exists()); + assert!(actual.err.contains("cannot remove non-empty directory")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_attempting_to_delete_single_dot_as_argument() { + Playground::setup("rm_test_7", |dirs, _| { + let actual = nu!( + cwd: dirs.root(), + "rm ." + ); + + assert!(actual.err.contains("cannot remove any parent directory")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn errors_if_attempting_to_delete_two_dot_as_argument() { + Playground::setup("rm_test_8", |dirs, _| { + let actual = nu!( + cwd: dirs.root(), + "rm .." + ); + + assert!(actual.err.contains("cannot remove any parent directory")); + }) +} + +#[test] +fn removes_multiple_directories() { + Playground::setup("rm_test_9", |dirs, sandbox| { + sandbox + .within("src") + .with_files(vec![EmptyFile("a.rs"), EmptyFile("b.rs")]) + .within("src/cli") + .with_files(vec![EmptyFile("c.rs"), EmptyFile("d.rs")]) + .within("test") + .with_files(vec![EmptyFile("a_test.rs"), EmptyFile("b_test.rs")]); + + nu!( + cwd: dirs.test(), + "rm src test --recursive" + ); + + assert_eq!( + Playground::glob_vec(&format!("{}/*", dirs.test().display())), + Vec::::new() + ); + }) +} + +#[test] +fn removes_multiple_files() { + Playground::setup("rm_test_10", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.txt"), + ]); + + nu!( + cwd: dirs.test(), + "rm yehuda.txt jonathan.txt andres.txt" + ); + + assert_eq!( + Playground::glob_vec(&format!("{}/*", dirs.test().display())), + Vec::::new() + ); + }) +} + +#[test] +fn removes_multiple_files_with_asterisks() { + Playground::setup("rm_test_11", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("andres.toml"), + ]); + + nu!( + cwd: dirs.test(), + "rm *.txt *.toml" + ); + + assert_eq!( + Playground::glob_vec(&format!("{}/*", dirs.test().display())), + Vec::::new() + ); + }) +} + +#[test] +fn allows_doubly_specified_file() { + Playground::setup("rm_test_12", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("yehuda.txt"), EmptyFile("jonathan.toml")]); + + let actual = nu!( + cwd: dirs.test(), + "rm *.txt yehuda* *.toml" + ); + + assert_eq!( + Playground::glob_vec(&format!("{}/*", dirs.test().display())), + Vec::::new() + ); + assert!(!actual.out.contains("error")) + }) +} + +#[test] +fn remove_files_from_two_parents_up_using_multiple_dots_and_glob() { + Playground::setup("rm_test_13", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("yehuda.txt"), + EmptyFile("jonathan.txt"), + EmptyFile("kevin.txt"), + ]); + + sandbox.within("foo").mkdir("bar"); + + nu!( + cwd: dirs.test().join("foo/bar"), + "rm .../*.txt" + ); + + assert!(!files_exist_at( + vec!["yehuda.txt", "jonathan.txt", "kevin.txt"], + dirs.test() + )); + }) +} + +#[test] +fn no_errors_if_attempting_to_delete_non_existent_file_with_f_flag() { + Playground::setup("rm_test_14", |dirs, _| { + let actual = nu!( + cwd: dirs.root(), + "rm -f non_existent_file.txt" + ); + + assert!(!actual.err.contains("no valid path")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn rm_wildcard_keeps_dotfiles() { + Playground::setup("rm_test_15", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("foo"), EmptyFile(".bar")]); + + nu!( + cwd: dirs.test(), + r#"rm *"# + ); + + assert!(!files_exist_at(vec!["foo"], dirs.test())); + assert!(files_exist_at(vec![".bar"], dirs.test())); + }) +} + +#[test] +fn rm_wildcard_leading_dot_deletes_dotfiles() { + Playground::setup("rm_test_16", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("foo"), EmptyFile(".bar")]); + + nu!( + cwd: dirs.test(), + r#"rm .*"# + ); + + assert!(files_exist_at(vec!["foo"], dirs.test())); + assert!(!files_exist_at(vec![".bar"], dirs.test())); + }) +} + +#[test] +fn removes_files_with_case_sensitive_glob_matches_by_default() { + Playground::setup("glob_test", |dirs, sandbox| { + sandbox.with_files(vec![EmptyFile("A0"), EmptyFile("a1")]); + + nu!( + cwd: dirs.root(), + "rm glob_test/A*" + ); + + let deleted_path = dirs.test().join("A0"); + let skipped_path = dirs.test().join("a1"); + + assert!(!deleted_path.exists()); + assert!(skipped_path.exists()); + }) +} diff --git a/crates/nu-command/tests/commands/roll.rs b/crates/nu-command/tests/commands/roll.rs new file mode 100644 index 0000000000..7230ecfb21 --- /dev/null +++ b/crates/nu-command/tests/commands/roll.rs @@ -0,0 +1,178 @@ +use nu_test_support::{nu, pipeline}; + +mod rows { + use super::*; + + fn table() -> String { + pipeline( + r#" + echo [ + [service, status]; + + [ruby, DOWN] + [db, DOWN] + [nud, DOWN] + [expected, HERE] + ]"#, + ) + } + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn roll_down_by_default() { + let actual = nu!( + cwd: ".", + format!("{} | {}", table(), pipeline(r#" + roll + | first + | get status + "#))); + + assert_eq!(actual.out, "HERE"); + } + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn can_roll_up() { + let actual = nu!( + cwd: ".", + format!("{} | {}", table(), pipeline(r#" + roll up 3 + | first + | get status + "#))); + + assert_eq!(actual.out, "HERE"); + } +} + +mod columns { + use super::*; + + fn table() -> String { + pipeline( + r#" + echo [ + [commit_author, origin, stars]; + + [ "Andres", EC, amarillito] + [ "Darren", US, black] + [ "Jonathan", US, black] + [ "Yehuda", US, black] + [ "Jason", CA, gold] + ]"#, + ) + } + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn roll_left_by_default() { + let actual = nu!( + cwd: ".", + format!("{} | {}", table(), pipeline(r#" + roll column + | get + | str collect "-" + "#))); + + assert_eq!(actual.out, "origin-stars-commit_author"); + } + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn can_roll_in_the_opposite_direction() { + let actual = nu!( + cwd: ".", + format!("{} | {}", table(), pipeline(r#" + roll column 2 --opposite + | get + | str collect "-" + "#))); + + assert_eq!(actual.out, "origin-stars-commit_author"); + } + + struct ThirtieTwo<'a>(usize, &'a str); + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn can_roll_the_cells_only_keeping_the_header_names() { + let four_bitstring = bitstring_to_nu_row_pipeline("00000100"); + let expected_value = ThirtieTwo(32, "bit1-bit2-bit3-bit4-bit5-bit6-bit7-bit8"); + + let actual = nu!( + cwd: ".", + format!("{} | roll column 3 --opposite --cells-only | get | str collect '-' ", four_bitstring) + ); + + assert_eq!(actual.out, expected_value.1); + } + + // FIXME: jt: needs more work + #[ignore] + #[test] + fn four_in_bitstring_left_shifted_with_three_bits_should_be_32_in_decimal() { + let four_bitstring = "00000100"; + let expected_value = ThirtieTwo(32, "00100000"); + + assert_eq!( + shift_three_bits_to_the_left_to_bitstring(four_bitstring), + expected_value.0.to_string() + ); + } + + fn shift_three_bits_to_the_left_to_bitstring(bits: &str) -> String { + // this pipeline takes the bitstring and outputs a nu row literal + // for example the number 4 in bitstring: + // + // input: 00000100 + // + // output: + // [ + // [Column1, Column2, Column3, Column4, Column5, Column6, Column7, Column8]; + // [ 0, 0, 0, 0, 0, 1, 0, 0] + // ] + // + let bitstring_as_nu_row_pipeline = bitstring_to_nu_row_pipeline(bits); + + // this pipeline takes the nu bitstring row literal, computes it's + // decimal value. + let nu_row_literal_bitstring_to_decimal_value_pipeline = pipeline( + r#" + pivot bit --ignore-titles + | get bit + | reverse + | each --numbered { + $it.item * (2 ** $it.index) + } + | math sum + "#, + ); + + nu!( + cwd: ".", + format!("{} | roll column 3 | {}", bitstring_as_nu_row_pipeline, nu_row_literal_bitstring_to_decimal_value_pipeline) + ).out + } + + fn bitstring_to_nu_row_pipeline(bits: &str) -> String { + format!( + "echo '{}' | {}", + bits, + pipeline( + r#" + split chars + | each { into int } + | rotate counter-clockwise _ + | reject _ + | rename bit1 bit2 bit3 bit4 bit5 bit6 bit7 bit8 + "# + ) + ) + } +} diff --git a/crates/nu-command/tests/commands/rotate.rs b/crates/nu-command/tests/commands/rotate.rs new file mode 100644 index 0000000000..906f7d9833 --- /dev/null +++ b/crates/nu-command/tests/commands/rotate.rs @@ -0,0 +1,85 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn counter_clockwise() { + let table = pipeline( + r#" + echo [ + [col1, col2, EXPECTED]; + + [---, "|||", XX1] + [---, "|||", XX2] + [---, "|||", XX3] + ] + "#, + ); + + let expected = nu!(cwd: ".", pipeline( + r#" + echo [ + [ Column0, Column1, Column2, Column3]; + + [ EXPECTED, XX1, XX2, XX3] + [ col2, "|||", "|||", "|||"] + [ col1, ---, ---, ---] + ] + | where Column0 == EXPECTED + | get Column1 Column2 Column3 + | str collect "-" + "#, + )); + + let actual = nu!( + cwd: ".", + format!("{} | {}", table, pipeline(r#" + rotate counter-clockwise + | where Column0 == EXPECTED + | get Column1 Column2 Column3 + | str collect "-" + "#))); + + assert_eq!(actual.out, expected.out); +} + +#[test] +fn clockwise() { + let table = pipeline( + r#" + echo [ + [col1, col2, EXPECTED]; + + [ ---, "|||", XX1] + [ ---, "|||", XX2] + [ ---, "|||", XX3] + ] + "#, + ); + + let expected = nu!(cwd: ".", pipeline( + r#" + echo [ + [ Column0, Column1, Column2, Column3]; + + [ ---, ---, ---, col1] + [ "|||", "|||", "|||", col2] + [ XX3, XX2, XX1, EXPECTED] + ] + | where Column3 == EXPECTED + | get Column0 Column1 Column2 + | str collect "-" + "#, + )); + + let actual = nu!( + cwd: ".", + format!("{} | {}", table, pipeline(r#" + rotate + | where Column3 == EXPECTED + | get Column0 Column1 Column2 + | str collect "-" + "#))); + + assert_eq!(actual.out, expected.out); +} diff --git a/crates/nu-command/tests/commands/save.rs b/crates/nu-command/tests/commands/save.rs new file mode 100644 index 0000000000..7a82867ad9 --- /dev/null +++ b/crates/nu-command/tests/commands/save.rs @@ -0,0 +1,69 @@ +use nu_test_support::fs::{file_contents, Stub::FileWithContent}; +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +#[test] +fn figures_out_intelligently_where_to_write_out_with_metadata() { + Playground::setup("save_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "cargo_sample.toml", + r#" + [package] + name = "nu" + version = "0.1.1" + authors = ["Yehuda Katz "] + description = "A shell for the GitHub era" + license = "ISC" + edition = "2018" + "#, + )]); + + let subject_file = dirs.test().join("cargo_sample.toml"); + + nu!( + cwd: dirs.root(), + "open save_test_1/cargo_sample.toml | save" + ); + + let actual = file_contents(&subject_file); + assert!(actual.contains("0.1.1")); + }) +} + +#[test] +fn writes_out_csv() { + Playground::setup("save_test_2", |dirs, sandbox| { + sandbox.with_files(vec![]); + + let expected_file = dirs.test().join("cargo_sample.csv"); + + nu!( + cwd: dirs.root(), + r#"echo [[name, version, description, license, edition]; [nu, "0.14", "A new type of shell", "MIT", "2018"]] | save save_test_2/cargo_sample.csv"#, + ); + + let actual = file_contents(expected_file); + println!("{}", actual); + assert!(actual.contains("nu,0.14,A new type of shell,MIT,2018")); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn save_append_will_create_file_if_not_exists() { + Playground::setup("save_test_3", |dirs, sandbox| { + sandbox.with_files(vec![]); + + let expected_file = dirs.test().join("new-file.txt"); + + nu!( + cwd: dirs.root(), + r#"echo hello | save --raw --append save_test_3/new-file.txt"#, + ); + + let actual = file_contents(expected_file); + println!("{}", actual); + assert!(actual == "hello"); + }) +} diff --git a/crates/nu-command/tests/commands/select.rs b/crates/nu-command/tests/commands/select.rs new file mode 100644 index 0000000000..6415445267 --- /dev/null +++ b/crates/nu-command/tests/commands/select.rs @@ -0,0 +1,128 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn regular_columns() { + let actual = nu!(cwd: ".", pipeline( + r#" + echo [ + [first_name, last_name, rusty_at, type]; + + [Andrés Robalino 10/11/2013 A] + [Jonathan Turner 10/12/2013 B] + [Yehuda Katz 10/11/2013 A] + ] + | select rusty_at last_name + | nth 0 + | get last_name + "# + )); + + assert_eq!(actual.out, "Robalino"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn complex_nested_columns() { + Playground::setup("select_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.json", + r#" + { + "nu": { + "committers": [ + {"name": "Andrés N. Robalino"}, + {"name": "Jonathan Turner"}, + {"name": "Yehuda Katz"} + ], + "releases": [ + {"version": "0.2"} + {"version": "0.8"}, + {"version": "0.9999999"} + ], + "0xATYKARNU": [ + ["Th", "e", " "], + ["BIG", " ", "UnO"], + ["punto", "cero"] + ] + } + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.json + | select nu."0xATYKARNU" nu.committers.name nu.releases.version + | where nu_releases_version > "0.8" + | get nu_releases_version + "# + )); + + assert_eq!(actual.out, "0.9999999"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn allows_if_given_unknown_column_name_is_missing() { + let actual = nu!(cwd: ".", pipeline( + r#" + echo [ + [first_name, last_name, rusty_at, type]; + + [Andrés Robalino 10/11/2013 A] + [Jonathan Turner 10/12/2013 B] + [Yehuda Katz 10/11/2013 A] + ] + | select rrusty_at first_name + | length + "# + )); + + assert_eq!(actual.out, "3"); +} + +#[test] +fn column_names_with_spaces() { + let actual = nu!(cwd: ".", pipeline( + r#" + echo [ + ["first name", "last name"]; + + [Andrés Robalino] + [Andrés Jnth] + ] + | select "last name" + | get "last name" + | str collect " " + "# + )); + + assert_eq!(actual.out, "Robalino Jnth"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn ignores_duplicate_columns_selected() { + let actual = nu!(cwd: ".", pipeline( + r#" + echo [ + ["first name", "last name"]; + + [Andrés Robalino] + [Andrés Jnth] + ] + | select "first name" "last name" "first name" + | get + | str collect " " + "# + )); + + assert_eq!(actual.out, "first name last name"); +} diff --git a/crates/nu-command/tests/commands/semicolon.rs b/crates/nu-command/tests/commands/semicolon.rs new file mode 100644 index 0000000000..19e4a9cfe2 --- /dev/null +++ b/crates/nu-command/tests/commands/semicolon.rs @@ -0,0 +1,29 @@ +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn semicolon_allows_lhs_to_complete() { + Playground::setup("create_test_1", |dirs, _sandbox| { + let actual = nu!( + cwd: dirs.test(), + "touch i_will_be_created_semi.txt; echo done" + ); + + let path = dirs.test().join("i_will_be_created_semi.txt"); + + assert!(path.exists()); + assert_eq!(actual.out, "done"); + }) +} + +#[test] +fn semicolon_lhs_error_stops_processing() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + where 1 1; echo done + "# + )); + + assert!(!actual.out.contains("done")); +} diff --git a/crates/nu-command/tests/commands/skip/mod.rs b/crates/nu-command/tests/commands/skip/mod.rs new file mode 100644 index 0000000000..aa35de0702 --- /dev/null +++ b/crates/nu-command/tests/commands/skip/mod.rs @@ -0,0 +1,2 @@ +mod until; +mod while_; diff --git a/crates/nu-command/tests/commands/skip/until.rs b/crates/nu-command/tests/commands/skip/until.rs new file mode 100644 index 0000000000..2003ed3c53 --- /dev/null +++ b/crates/nu-command/tests/commands/skip/until.rs @@ -0,0 +1,51 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn condition_is_met() { + Playground::setup("skip_until_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "caballeros.txt", + r#" + CHICKEN SUMMARY report date: April 29th, 2020 + -------------------------------------------------------------------- + Chicken Collection,29/04/2020,30/04/2020,31/04/2020 + Yellow Chickens,,, + Andrés,0,0,1 + Jonathan,0,0,1 + Jason,0,0,1 + Yehuda,0,0,1 + Blue Chickens,,, + Andrés,0,0,1 + Jonathan,0,0,1 + Jason,0,0,1 + Yehuda,0,0,2 + Red Chickens,,, + Andrés,0,0,1 + Jonathan,0,0,1 + Jason,0,0,1 + Yehuda,0,0,3 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open --raw ./caballeros.txt + | lines + | skip 2 + | str trim + | str collect (char nl) + | from csv + | skip until "Chicken Collection" == "Red Chickens" + | skip 1 + | into int "31/04/2020" + | get "31/04/2020" + | math sum + "# + )); + + assert_eq!(actual.out, "6"); + }) +} diff --git a/crates/nu-command/tests/commands/skip/while_.rs b/crates/nu-command/tests/commands/skip/while_.rs new file mode 100644 index 0000000000..a2cad635fc --- /dev/null +++ b/crates/nu-command/tests/commands/skip/while_.rs @@ -0,0 +1,51 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn condition_is_met() { + Playground::setup("skip_while_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "caballeros.txt", + r#" + CHICKEN SUMMARY report date: April 29th, 2020 + -------------------------------------------------------------------- + Chicken Collection,29/04/2020,30/04/2020,31/04/2020 + Yellow Chickens,,, + Andrés,0,0,1 + Jonathan,0,0,1 + Jason,0,0,1 + Yehuda,0,0,1 + Blue Chickens,,, + Andrés,0,0,1 + Jonathan,0,0,1 + Jason,0,0,1 + Yehuda,0,0,2 + Red Chickens,,, + Andrés,0,0,1 + Jonathan,0,0,1 + Jason,0,0,1 + Yehuda,0,0,3 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open --raw caballeros.txt + | lines + | skip 2 + | str trim + | str collect (char nl) + | from csv + | skip while "Chicken Collection" != "Red Chickens" + | skip 1 + | into int "31/04/2020" + | get "31/04/2020" + | math sum + "# + )); + + assert_eq!(actual.out, "6"); + }) +} diff --git a/crates/nu-command/tests/commands/sort_by.rs b/crates/nu-command/tests/commands/sort_by.rs new file mode 100644 index 0000000000..c3bc4f4238 --- /dev/null +++ b/crates/nu-command/tests/commands/sort_by.rs @@ -0,0 +1,160 @@ +use nu_test_support::{nu, pipeline}; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn by_column() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml --raw + | lines + | skip 1 + | first 4 + | split column "=" + | sort-by Column1 + | skip 1 + | first 1 + | get Column1 + | str trim + "# + )); + + assert_eq!(actual.out, "description"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn by_invalid_column() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml --raw + | lines + | skip 1 + | first 4 + | split column "=" + | sort-by ColumnThatDoesNotExist + | skip 1 + | first 1 + | get Column1 + | str trim + "# + )); + + assert!(actual.err.contains("Can not find column to sort by")); + assert!(actual.err.contains("invalid column")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn by_invalid_types() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml --raw + | echo [1 "foo"] + | sort-by + "# + )); + + assert!(actual.err.contains("Not all values can be compared")); + assert!(actual + .err + .contains("Unable to sort values, as \"integer\" cannot compare against \"string\"")); +} + +#[test] +fn sort_primitive_values() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml --raw + | lines + | skip 1 + | first 6 + | sort-by + | first 1 + "# + )); + + assert_eq!(actual.out, "authors = [\"The Nu Project Contributors\"]"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn ls_sort_by_name_sensitive() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample-ls-output.json + | sort-by name + | select name + | to json --raw + "# + )); + + let json_output = r#"[{"name":"B.txt"},{"name":"C"},{"name":"a.txt"}]"#; + + assert_eq!(actual.out, json_output); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn ls_sort_by_name_insensitive() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample-ls-output.json + | sort-by -i name + | select name + | to json --raw + "# + )); + + let json_output = r#"[{"name":"a.txt"},{"name":"B.txt"},{"name":"C"}]"#; + + assert_eq!(actual.out, json_output); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn ls_sort_by_type_name_sensitive() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample-ls-output.json + | sort-by type name + | select name type + | to json --raw + "# + )); + + let json_output = r#"[{"name":"C","type":"Dir"},{"name":"B.txt","type":"File"},{"name":"a.txt","type":"File"}]"#; + + assert_eq!(actual.out, json_output); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn ls_sort_by_type_name_insensitive() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample-ls-output.json + | sort-by -i type name + | select name type + | to json --raw + "# + )); + + let json_output = r#"[{"name":"C","type":"Dir"},{"name":"a.txt","type":"File"},{"name":"B.txt","type":"File"}]"#; + + assert_eq!(actual.out, json_output); +} diff --git a/crates/nu-command/tests/commands/source.rs b/crates/nu-command/tests/commands/source.rs new file mode 100644 index 0000000000..dcc342d832 --- /dev/null +++ b/crates/nu-command/tests/commands/source.rs @@ -0,0 +1,153 @@ +use nu_test_support::fs::{AbsolutePath, DisplayPath, Stub::FileWithContent}; +use nu_test_support::nu; +use nu_test_support::pipeline; +use nu_test_support::playground::Playground; + +#[should_panic] +#[test] +fn sources_also_files_under_custom_lib_dirs_path() { + Playground::setup("source_test_1", |dirs, nu| { + let file = AbsolutePath::new(dirs.test().join("config.toml")); + let library_path = AbsolutePath::new(dirs.test().join("lib")); + + nu.with_config(&file); + nu.with_files(vec![FileWithContent( + "config.toml", + &format!( + r#" + lib_dirs = ["{}"] + skip_welcome_message = true + "#, + library_path.display_path() + ), + )]); + + nu.within("lib").with_files(vec![FileWithContent( + "my_library.nu", + r#" + source my_library/main.nu + "#, + )]); + nu.within("lib/my_library").with_files(vec![FileWithContent( + "main.nu", + r#" + def hello [] { + echo "hello nu" + } + "#, + )]); + + let actual = nu!( + cwd: ".", pipeline( + r#" + source my_library.nu ; + + hello + "# + )); + + assert_eq!(actual.out, "hello nu"); + }) +} + +fn try_source_foo_with_double_quotes_in(testdir: &str, playdir: &str) { + Playground::setup(playdir, |dirs, sandbox| { + let testdir = String::from(testdir); + let mut foo_file = testdir.clone(); + foo_file.push_str("/foo.nu"); + + sandbox.mkdir(&testdir); + sandbox.with_files(vec![FileWithContent(&foo_file, "echo foo")]); + + let cmd = String::from("source ") + r#"""# + &foo_file + r#"""#; + + let actual = nu!(cwd: dirs.test(), &cmd); + + assert_eq!(actual.out, "foo"); + }); +} + +fn try_source_foo_with_single_quotes_in(testdir: &str, playdir: &str) { + Playground::setup(playdir, |dirs, sandbox| { + let testdir = String::from(testdir); + let mut foo_file = testdir.clone(); + foo_file.push_str("/foo.nu"); + + sandbox.mkdir(&testdir); + sandbox.with_files(vec![FileWithContent(&foo_file, "echo foo")]); + + let cmd = String::from("source ") + r#"'"# + &foo_file + r#"'"#; + + let actual = nu!(cwd: dirs.test(), &cmd); + + assert_eq!(actual.out, "foo"); + }); +} + +fn try_source_foo_without_quotes_in(testdir: &str, playdir: &str) { + Playground::setup(playdir, |dirs, sandbox| { + let testdir = String::from(testdir); + let mut foo_file = testdir.clone(); + foo_file.push_str("/foo.nu"); + + sandbox.mkdir(&testdir); + sandbox.with_files(vec![FileWithContent(&foo_file, "echo foo")]); + + let cmd = String::from("source ") + &foo_file; + + let actual = nu!(cwd: dirs.test(), &cmd); + + assert_eq!(actual.out, "foo"); + }); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sources_unicode_file_in_normal_dir() { + try_source_foo_with_single_quotes_in("foo", "source_test_1"); + try_source_foo_with_double_quotes_in("foo", "source_test_2"); + try_source_foo_without_quotes_in("foo", "source_test_3"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sources_unicode_file_in_unicode_dir_without_spaces_1() { + try_source_foo_with_single_quotes_in("🚒", "source_test_4"); + try_source_foo_with_double_quotes_in("🚒", "source_test_5"); + try_source_foo_without_quotes_in("🚒", "source_test_6"); +} + +// FIXME: jt: needs more work +#[ignore] +#[cfg(not(windows))] // ':' is not allowed in Windows paths +#[test] +fn sources_unicode_file_in_unicode_dir_without_spaces_2() { + try_source_foo_with_single_quotes_in(":fire_engine:", "source_test_7"); + try_source_foo_with_double_quotes_in(":fire_engine:", "source_test_8"); + try_source_foo_without_quotes_in(":fire_engine:", "source_test_9"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn sources_unicode_file_in_unicode_dir_with_spaces_1() { + try_source_foo_with_single_quotes_in("e-$ èрт🚒♞中片-j", "source_test_8"); + try_source_foo_with_double_quotes_in("e-$ èрт🚒♞中片-j", "source_test_9"); +} + +// FIXME: jt: needs more work +#[ignore] +#[cfg(not(windows))] // ':' is not allowed in Windows paths +#[test] +fn sources_unicode_file_in_unicode_dir_with_spaces_2() { + try_source_foo_with_single_quotes_in("e-$ èрт:fire_engine:♞中片-j", "source_test_10"); + try_source_foo_with_double_quotes_in("e-$ èрт:fire_engine:♞中片-j", "source_test_11"); +} + +#[ignore] +#[test] +fn sources_unicode_file_in_non_utf8_dir() { + // How do I create non-UTF-8 path??? +} diff --git a/crates/nu-command/tests/commands/split_by.rs b/crates/nu-command/tests/commands/split_by.rs new file mode 100644 index 0000000000..7cfede1a14 --- /dev/null +++ b/crates/nu-command/tests/commands/split_by.rs @@ -0,0 +1,54 @@ +use nu_test_support::fs::Stub::{EmptyFile, FileWithContentToBeTrimmed}; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn splits() { + Playground::setup("split_by_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at,type + Andrés,Robalino,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | group-by rusty_at + | split-by type + | get A."10/11/2013" + | length + "# + )); + + assert_eq!(actual.out, "2"); + }) +} + +#[test] +fn errors_if_no_table_given_as_input() { + Playground::setup("split_by_test_2", |dirs, sandbox| { + sandbox.with_files(vec![ + EmptyFile("los.txt"), + EmptyFile("tres.txt"), + EmptyFile("amigos.txt"), + EmptyFile("arepas.clu"), + ]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + ls + | get name + | split-by type + "# + )); + + assert!(actual.err.contains("requires a table")); + }) +} diff --git a/crates/nu-command/tests/commands/split_column.rs b/crates/nu-command/tests/commands/split_column.rs new file mode 100644 index 0000000000..cb591e5002 --- /dev/null +++ b/crates/nu-command/tests/commands/split_column.rs @@ -0,0 +1,28 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn to_column() { + Playground::setup("split_column_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.txt", + r#" + importer,shipper,tariff_item,name,origin + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.txt + | lines + | str trim + | split column "," + | get Column2 + "# + )); + + assert!(actual.out.contains("shipper")); + }) +} diff --git a/crates/nu-command/tests/commands/split_row.rs b/crates/nu-command/tests/commands/split_row.rs new file mode 100644 index 0000000000..e7e7dd4f37 --- /dev/null +++ b/crates/nu-command/tests/commands/split_row.rs @@ -0,0 +1,28 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn to_row() { + Playground::setup("split_row_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.txt", + r#" + importer,shipper,tariff_item,name,origin + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.txt + | lines + | str trim + | split row "," + | length + "# + )); + + assert!(actual.out.contains('5')); + }) +} diff --git a/crates/nu-command/tests/commands/str_/collect.rs b/crates/nu-command/tests/commands/str_/collect.rs new file mode 100644 index 0000000000..73db93cf13 --- /dev/null +++ b/crates/nu-command/tests/commands/str_/collect.rs @@ -0,0 +1,53 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn test_1() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1..5 | into string | str collect + "# + ) + ); + + assert_eq!(actual.out, "12345"); +} + +#[test] +fn test_2() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [a b c d] | str collect "" + "# + ) + ); + + assert_eq!(actual.out, "abcd"); +} + +#[test] +fn construct_a_path() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [sample txt] | str collect "." + "# + ) + ); + + assert_eq!(actual.out, "sample.txt"); +} + +#[test] +fn sum_one_to_four() { + let actual = nu!( + cwd: ".", pipeline( + r#" + 1..4 | each { $it } | into string | str collect "+" | math eval + "# + ) + ); + + assert!(actual.out.contains("10")); +} diff --git a/crates/nu-command/tests/commands/str_/into_string.rs b/crates/nu-command/tests/commands/str_/into_string.rs new file mode 100644 index 0000000000..9a50bcd6ac --- /dev/null +++ b/crates/nu-command/tests/commands/str_/into_string.rs @@ -0,0 +1,158 @@ +use nu_test_support::playground::{Dirs, Playground}; +use nu_test_support::{nu, pipeline}; + +#[test] +fn from_range() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1..5 | into string | to json + "# + ) + ); + + assert_eq!(actual.out, "[\"1\",\"2\",\"3\",\"4\",\"5\"]"); +} + +#[test] +fn from_number() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 5 | into string + "# + ) + ); + + assert_eq!(actual.out, "5"); +} + +#[test] +fn from_decimal() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 1.5 | into string + "# + ) + ); + + assert_eq!(actual.out, "1.5"); +} + +#[test] +fn from_boolean() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo $true | into string + "# + ) + ); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn from_string() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "one" | into string + "# + ) + ); + + assert_eq!(actual.out, "one"); +} + +#[test] +fn from_filename() { + Playground::setup("from_filename", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.toml", + r#" + [dependency] + name = "nu" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "ls sample.toml | get name | into string" + ); + + assert_eq!(actual.out, "sample.toml"); + }) +} + +#[test] +fn from_filesize() { + Playground::setup("from_filesize", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.toml", + r#" + [dependency] + name = "nu" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "ls sample.toml | get size | into string" + ); + + assert_eq!(actual.out, "25 B"); + }) +} + +#[test] +fn from_decimal_correct_trailing_zeros() { + let actual = nu!( + cwd: ".", pipeline( + r#" + = 1.23000 | into string -d 3 + "# + )); + + assert!(actual.out.contains("1.230")); +} + +#[test] +fn from_int_decimal_correct_trailing_zeros() { + let actual = nu!( + cwd: ".", pipeline( + r#" + = 1.00000 | into string -d 3 + "# + )); + + assert!(actual.out.contains("1.000")); +} + +#[test] +fn from_int_decimal_trim_trailing_zeros() { + let actual = nu!( + cwd: ".", pipeline( + r#" + = 1.00000 | into string | format "{$it} flat" + "# + )); + + assert!(actual.out.contains("1 flat")); // "1" would match "1.0" +} + +#[test] +fn from_table() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo '[{"name": "foo", "weight": 32.377}, {"name": "bar", "weight": 15.2}]' + | from json + | into string weight -d 2 + "# + )); + + assert!(actual.out.contains("32.38")); + assert!(actual.out.contains("15.20")); +} diff --git a/crates/nu-command/tests/commands/str_/mod.rs b/crates/nu-command/tests/commands/str_/mod.rs new file mode 100644 index 0000000000..9f8c23d5fc --- /dev/null +++ b/crates/nu-command/tests/commands/str_/mod.rs @@ -0,0 +1,356 @@ +mod collect; + +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn trims() { + Playground::setup("str_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependency] + name = "nu " + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str trim dependency.name | get dependency.name" + ); + + assert_eq!(actual.out, "nu"); + }) +} + +#[test] +fn error_trim_multiple_chars() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "does it work now?!" | str trim -c "?!" + "# + ) + ); + + assert!(actual.err.contains("char")); +} + +#[test] +fn capitalizes() { + Playground::setup("str_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependency] + name = "nu" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str capitalize dependency.name | get dependency.name" + ); + + assert_eq!(actual.out, "Nu"); + }) +} + +#[test] +fn downcases() { + Playground::setup("str_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependency] + name = "LIGHT" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str downcase dependency.name | get dependency.name" + ); + + assert_eq!(actual.out, "light"); + }) +} + +#[test] +fn upcases() { + Playground::setup("str_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nushell" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str upcase package.name | get package.name" + ); + + assert_eq!(actual.out, "NUSHELL"); + }) +} + +#[test] +fn camelcases() { + Playground::setup("str_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [dependency] + name = "THIS_IS_A_TEST" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open sample.toml | str camel-case dependency.name | get dependency.name" + ); + + assert_eq!(actual.out, "thisIsATest"); + }) +} + +#[test] +fn converts_to_int() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo '{number_as_string: "1"}' + | from json + | into int number_as_string + | rename number + | where number == 1 + | get number + + "# + )); + + assert_eq!(actual.out, "1"); +} + +#[test] +fn converts_to_decimal() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo "3.1, 0.0415" + | split row "," + | into decimal + | math sum + "# + )); + + assert_eq!(actual.out, "3.1415"); +} + +#[test] +fn find_and_replaces() { + Playground::setup("str_test_6", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [fortune.teller] + phone = "1-800-KATZ" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str find-replace KATZ "5289" fortune.teller.phone + | get fortune.teller.phone + "# + )); + + assert_eq!(actual.out, "1-800-5289"); + }) +} + +#[test] +fn find_and_replaces_without_passing_field() { + Playground::setup("str_test_7", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [fortune.teller] + phone = "1-800-KATZ" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | get fortune.teller.phone + | str find-replace KATZ "5289" + "# + )); + + assert_eq!(actual.out, "1-800-5289"); + }) +} + +#[test] +fn substrings_the_input() { + Playground::setup("str_test_8", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [fortune.teller] + phone = "1-800-ROBALINO" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str substring 6,14 fortune.teller.phone + | get fortune.teller.phone + "# + )); + + assert_eq!(actual.out, "ROBALINO"); + }) +} + +#[test] +fn substring_errors_if_start_index_is_greater_than_end_index() { + Playground::setup("str_test_9", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [fortune.teller] + phone = "1-800-ROBALINO" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str substring 6,5 fortune.teller.phone + "# + )); + + assert!(actual + .err + .contains("End must be greater than or equal to Start")) + }) +} + +#[test] +fn substrings_the_input_and_returns_the_string_if_end_index_exceeds_length() { + Playground::setup("str_test_10", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nu-arepas" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str substring 0,999 package.name + | get package.name + "# + )); + + assert_eq!(actual.out, "nu-arepas"); + }) +} + +#[test] +fn substrings_the_input_and_returns_blank_if_start_index_exceeds_length() { + Playground::setup("str_test_11", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nu-arepas" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str substring 50,999 package.name + | get package.name + "# + )); + + assert_eq!(actual.out, ""); + }) +} + +#[test] +fn substrings_the_input_and_treats_start_index_as_zero_if_blank_start_index_given() { + Playground::setup("str_test_12", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nu-arepas" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str substring ,2 package.name + | get package.name + "# + )); + + assert_eq!(actual.out, "nu"); + }) +} + +#[test] +fn substrings_the_input_and_treats_end_index_as_length_if_blank_end_index_given() { + Playground::setup("str_test_13", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContent( + "sample.toml", + r#" + [package] + name = "nu-arepas" + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.toml + | str substring 3, package.name + | get package.name + "# + )); + + assert_eq!(actual.out, "arepas"); + }) +} + +#[test] +fn str_reverse() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "nushell" | str reverse + "# + )); + + assert!(actual.out.contains("llehsun")); +} diff --git a/crates/nu-command/tests/commands/touch.rs b/crates/nu-command/tests/commands/touch.rs new file mode 100644 index 0000000000..affc1d14cc --- /dev/null +++ b/crates/nu-command/tests/commands/touch.rs @@ -0,0 +1,31 @@ +use nu_test_support::nu; +use nu_test_support::playground::Playground; + +#[test] +fn creates_a_file_when_it_doesnt_exist() { + Playground::setup("create_test_1", |dirs, _sandbox| { + nu!( + cwd: dirs.test(), + "touch i_will_be_created.txt" + ); + + let path = dirs.test().join("i_will_be_created.txt"); + assert!(path.exists()); + }) +} + +#[test] +fn creates_two_files() { + Playground::setup("create_test_2", |dirs, _sandbox| { + nu!( + cwd: dirs.test(), + "touch a b" + ); + + let path = dirs.test().join("a"); + assert!(path.exists()); + + let path2 = dirs.test().join("b"); + assert!(path2.exists()); + }) +} diff --git a/crates/nu-command/tests/commands/uniq.rs b/crates/nu-command/tests/commands/uniq.rs new file mode 100644 index 0000000000..dd7f433a97 --- /dev/null +++ b/crates/nu-command/tests/commands/uniq.rs @@ -0,0 +1,234 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn removes_duplicate_rows() { + Playground::setup("uniq_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at,type + Andrés,Robalino,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | uniq + | length + + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn uniq_values() { + Playground::setup("uniq_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.csv", + r#" + first_name,last_name,rusty_at,type + Andrés,Robalino,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + Jonathan,Turner,10/12/2013,B + Yehuda,Katz,10/11/2013,A + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.csv + | select type + | uniq + | length + + "# + )); + + assert_eq!(actual.out, "2"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn nested_json_structures() { + Playground::setup("uniq_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "nested_json_structures.json", + r#" + [ + { + "name": "this is duplicated", + "nesting": [ { "a": "a", "b": "b" }, + { "c": "c", "d": "d" } + ], + "can_be_ordered_differently": { + "array": [1, 2, 3, 4, 5], + "something": { "else": "works" } + } + }, + { + "can_be_ordered_differently": { + "something": { "else": "works" }, + "array": [1, 2, 3, 4, 5] + }, + "nesting": [ { "b": "b", "a": "a" }, + { "d": "d", "c": "c" } + ], + "name": "this is duplicated" + }, + { + "name": "this is unique", + "nesting": [ { "a": "b", "b": "a" }, + { "c": "d", "d": "c" } + ], + "can_be_ordered_differently": { + "array": [], + "something": { "else": "does not work" } + } + }, + { + "name": "this is unique", + "nesting": [ { "a": "a", "b": "b", "c": "c" }, + { "d": "d", "e": "e", "f": "f" } + ], + "can_be_ordered_differently": { + "array": [], + "something": { "else": "works" } + } + } + ] + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open nested_json_structures.json + | uniq + | length + + "# + )); + assert_eq!(actual.out, "3"); + }) +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn uniq_when_keys_out_of_order() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + [{"a": "a", "b": [1,2,3]}, {"b": [1,2,3], "a": "a"}] + | uniq + | length + + "# + )); + + assert_eq!(actual.out, "1"); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn uniq_counting() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + ["A", "B", "A"] + | wrap item + | uniq --count + | where item == A + | get count + "# + )); + assert_eq!(actual.out, "2"); + + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo ["A", "B", "A"] + | wrap item + | uniq --count + | where item == B + | get count + "# + )); + assert_eq!(actual.out, "1"); +} + +#[test] +fn uniq_unique() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3 4 1 5] + | uniq --unique + "# + )); + let expected = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [2 3 4 5] + "# + )); + print!("{}", actual.out); + print!("{}", expected.out); + assert_eq!(actual.out, expected.out); +} + +#[test] +fn uniq_simple_vals_ints() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3 4 1 5] + | uniq + "# + )); + let expected = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [1 2 3 4 5] + "# + )); + print!("{}", actual.out); + print!("{}", expected.out); + assert_eq!(actual.out, expected.out); +} + +#[test] +fn uniq_simple_vals_strs() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [A B C A] + | uniq + "# + )); + let expected = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + echo [A B C] + "# + )); + print!("{}", actual.out); + print!("{}", expected.out); + assert_eq!(actual.out, expected.out); +} diff --git a/crates/nu-command/tests/commands/update.rs b/crates/nu-command/tests/commands/update.rs new file mode 100644 index 0000000000..5506fb4880 --- /dev/null +++ b/crates/nu-command/tests/commands/update.rs @@ -0,0 +1,60 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn sets_the_column() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml + | update dev-dependencies.pretty_assertions "0.7.0" + | get dev-dependencies.pretty_assertions + "# + )); + + assert_eq!(actual.out, "0.7.0"); +} + +#[cfg(features = "inc")] +#[test] +fn sets_the_column_from_a_block_run_output() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml + | update dev-dependencies.pretty_assertions { open cargo_sample.toml | get dev-dependencies.pretty_assertions | inc --minor } + | get dev-dependencies.pretty_assertions + "# + )); + + assert_eq!(actual.out, "0.7.0"); +} + +#[test] +fn sets_the_column_from_a_block_full_stream_output() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + wrap content + | update content { open --raw cargo_sample.toml | lines | first 5 } + | get content.1 + | str contains "nu" + "# + )); + + assert_eq!(actual.out, "true"); +} + +#[test] +fn sets_the_column_from_a_subexpression() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + wrap content + | update content (open --raw cargo_sample.toml | lines | first 5) + | get content.1 + | str contains "nu" + "# + )); + + assert_eq!(actual.out, "true"); +} diff --git a/crates/nu-command/tests/commands/where_.rs b/crates/nu-command/tests/commands/where_.rs new file mode 100644 index 0000000000..c4eb69c639 --- /dev/null +++ b/crates/nu-command/tests/commands/where_.rs @@ -0,0 +1,166 @@ +use nu_test_support::nu; + +#[cfg(feature = "sqlite")] +use nu_test_support::pipeline; + +#[test] +fn filters_by_unit_size_comparison() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "ls | where size > 1kib | sort-by size | get name | first 1 | str trim" + ); + + assert_eq!(actual.out, "cargo_sample.toml"); +} + +#[test] +fn filters_with_nothing_comparison() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r#"echo '[{"foo": 3}, {"foo": null}, {"foo": 4}]' | from json | get foo | compact | where $it > 1 | math sum"# + ); + + assert_eq!(actual.out, "7"); +} + +#[test] +fn where_in_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name in ["foo"] | get size | math sum"# + ); + + assert_eq!(actual.out, "5"); +} + +#[test] +fn where_not_in_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r#"echo '[{"name": "foo", "size": 3}, {"name": "foo", "size": 2}, {"name": "bar", "size": 4}]' | from json | where name not-in ["foo"] | get size | math sum"# + ); + + assert_eq!(actual.out, "4"); +} + +#[cfg(feature = "sqlite")] +#[test] +fn explicit_block_condition() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == ints + | get table_values + | first 4 + | where {= $it.z > 4200} + | get z + "# + )); + + assert_eq!(actual.out, "4253"); +} + +#[cfg(feature = "sqlite")] +#[test] +fn binary_operator_comparisons() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == ints + | get table_values + | first 4 + | where z > 4200 + | get z + "# + )); + + assert_eq!(actual.out, "4253"); + + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == ints + | get table_values + | first 4 + | where z >= 4253 + | get z + "# + )); + + assert_eq!(actual.out, "4253"); + + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == ints + | get table_values + | first 4 + | where z < 10 + | get z + "# + )); + + assert_eq!(actual.out, "1"); + + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == ints + | get table_values + | first 4 + | where z <= 1 + | get z + "# + )); + + assert_eq!(actual.out, "1"); + + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == ints + | get table_values + | where z != 1 + | first 1 + | get z + "# + )); + + assert_eq!(actual.out, "42"); +} + +#[cfg(feature = "sqlite")] +#[test] +fn contains_operator() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == strings + | get table_values + | where x =~ ell + | length + "# + )); + + assert_eq!(actual.out, "4"); + + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | where table_name == strings + | get table_values + | where x !~ ell + | length + "# + )); + + assert_eq!(actual.out, "2"); +} diff --git a/crates/nu-command/tests/commands/which.rs b/crates/nu-command/tests/commands/which.rs new file mode 100644 index 0000000000..a16e571c52 --- /dev/null +++ b/crates/nu-command/tests/commands/which.rs @@ -0,0 +1,96 @@ +use nu_test_support::nu; + +#[test] +fn which_ls() { + let actual = nu!( + cwd: ".", + "which ls | get path | str trim" + ); + + assert_eq!(actual.out, "Nushell built-in command"); +} + +#[test] +fn which_alias_ls() { + let actual = nu!( + cwd: ".", + "alias ls = ls -a; which ls | get path | str trim" + ); + + assert_eq!(actual.out, "Nushell alias: ls -a"); +} + +#[test] +fn which_def_ls() { + let actual = nu!( + cwd: ".", + "def ls [] {echo def}; which ls | get path | str trim" + ); + + assert_eq!(actual.out, "Nushell custom command"); +} + +#[test] +fn correct_precedence_alias_def_custom() { + let actual = nu!( + cwd: ".", + "def ls [] {echo def}; alias ls = echo alias; which ls | get path | str trim" + ); + + assert_eq!(actual.out, "Nushell alias: echo alias"); +} + +#[test] +fn multiple_reports_for_alias_def_custom() { + let actual = nu!( + cwd: ".", + "def ls [] {echo def}; alias ls = echo alias; which -a ls | length" + ); + + let length: i32 = actual.out.parse().unwrap(); + assert!(length >= 3); +} + +// `get_aliases_with_name` and `get_custom_commands_with_name` don't return the correct count of +// values +// I suspect this is due to the ScopeFrame getting discarded at '}' and the command is then +// executed in the parent scope +// See: parse_definition, line 2187 for reference. +#[ignore] +#[test] +fn multiple_reports_of_multiple_alias() { + let actual = nu!( + cwd: ".", + "alias xaz = echo alias1; def helper [] {alias xaz = echo alias2; which -a xaz}; helper | length" + ); + + let length: i32 = actual.out.parse().unwrap(); + assert_eq!(length, 2); +} + +#[ignore] +#[test] +fn multiple_reports_of_multiple_defs() { + let actual = nu!( + cwd: ".", + "def xaz [] {echo def1}; def helper [] { def xaz [] { echo def2 }; which -a xaz }; helper | length" + ); + + let length: i32 = actual.out.parse().unwrap(); + assert_eq!(length, 2); +} + +//Fails due to ParserScope::add_definition +// frame.custom_commands.insert(name.clone(), block.clone()); +// frame.commands.insert(name, whole_stream_command(block)); +#[ignore] +#[test] +fn def_only_seen_once() { + let actual = nu!( + cwd: ".", + "def xaz [] {echo def1}; which -a xaz | length" + ); + //length is 2. One custom_command (def) one built in ("wrongly" added) + let length: i32 = actual.out.parse().unwrap(); + assert_eq!(length, 1); +} diff --git a/crates/nu-command/tests/commands/with_env.rs b/crates/nu-command/tests/commands/with_env.rs new file mode 100644 index 0000000000..4f6480bdb6 --- /dev/null +++ b/crates/nu-command/tests/commands/with_env.rs @@ -0,0 +1,106 @@ +use nu_test_support::nu; + +#[test] +fn with_env_extends_environment() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "with-env [FOO BARRRR] {echo $env} | get FOO" + ); + + assert_eq!(actual.out, "BARRRR"); +} + +#[test] +fn with_env_shorthand() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "FOO=BARRRR echo $env | get FOO" + ); + + assert_eq!(actual.out, "BARRRR"); +} + +#[test] +fn shorthand_doesnt_reorder_arguments() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "FOO=BARRRR nu --testbin cococo first second" + ); + + assert_eq!(actual.out, "first second"); +} + +#[test] +fn with_env_shorthand_trims_quotes() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "FOO='BARRRR' echo $env | get FOO" + ); + + assert_eq!(actual.out, "BARRRR"); +} + +#[test] +fn with_env_and_shorthand_same_result() { + let actual_shorthand = nu!( + cwd: "tests/fixtures/formats", + "FOO='BARRRR' echo $env | get FOO" + ); + + let actual_normal = nu!( + cwd: "tests/fixtures/formats", + "with-env [FOO BARRRR] {echo $env} | get FOO" + ); + + assert_eq!(actual_shorthand.out, actual_normal.out); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn with_env_shorthand_nested_quotes() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "FOO='-arg \"hello world\"' echo $env | get FOO" + ); + + assert_eq!(actual.out, "-arg \"hello world\""); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn with_env_hides_variables_in_parent_scope() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r#" + let-env FOO = "1" + echo $env.FOO + with-env [FOO $nothing] { + echo $env.FOO + } + echo $env.FOO + "# + ); + + assert_eq!(actual.out, "11"); + assert!(actual.err.contains("error")); + assert!(actual.err.contains("Unknown column")); +} + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn with_env_shorthand_can_not_hide_variables() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r#" + let-env FOO = "1" + echo $env.FOO + FOO=$nothing echo $env.FOO + echo $env.FOO + "# + ); + + assert_eq!(actual.out, "1$nothing1"); +} diff --git a/crates/nu-command/tests/commands/wrap.rs b/crates/nu-command/tests/commands/wrap.rs new file mode 100644 index 0000000000..968e5f1256 --- /dev/null +++ b/crates/nu-command/tests/commands/wrap.rs @@ -0,0 +1,61 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn wrap_rows_into_a_row() { + Playground::setup("wrap_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.txt", + r#" + first_name,last_name + Andrés,Robalino + Jonathan,Turner + Yehuda,Katz + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.txt + | from csv + | wrap caballeros + | get caballeros + | nth 0 + | get last_name + "# + )); + + assert_eq!(actual.out, "Robalino"); + }) +} + +#[test] +fn wrap_rows_into_a_table() { + Playground::setup("wrap_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.txt", + r#" + first_name,last_name + Andrés,Robalino + Jonathan,Turner + Yehuda,Katz + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.txt + | from csv + | get last_name + | wrap caballero + | nth 2 + | get caballero + "# + )); + + assert_eq!(actual.out, "Katz"); + }) +} diff --git a/crates/nu-command/tests/commands/zip.rs b/crates/nu-command/tests/commands/zip.rs new file mode 100644 index 0000000000..903294dca5 --- /dev/null +++ b/crates/nu-command/tests/commands/zip.rs @@ -0,0 +1,73 @@ +use nu_test_support::fs::Stub::FileWithContent; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +const ZIP_POWERED_TEST_ASSERTION_SCRIPT: &str = r#" +def expect [ + left, + right, + --to-eq +] { + $left | zip { $right } | all? { + $it.name.0 == $it.name.1 && $it.commits.0 == $it.commits.1 + } +} + +def add-commits [n] { + each { + let contributor = $it; + let name = $it.name; + let commits = $it.commits; + + $contributor | merge { + [[commits]; [($commits + $n)]] + } + } +} +"#; + +// FIXME: jt: needs more work +#[ignore] +#[test] +fn zips_two_tables() { + Playground::setup("zip_test_1", |dirs, nu| { + nu.with_files(vec![FileWithContent( + "zip_test.nu", + &format!("{}\n", ZIP_POWERED_TEST_ASSERTION_SCRIPT), + )]); + + let actual = nu!( + cwd: ".", pipeline( + &format!( + r#" + source {} ; + + let contributors = ([ + [name, commits]; + [andres, 10] + [ jt, 20] + ]); + + let actual = ($contributors | add-commits 10); + + expect $actual --to-eq [[name, commits]; [andres, 20] [jt, 30]] + "#, + dirs.test().join("zip_test.nu").display() + ) + )); + + assert_eq!(actual.out, "true"); + }) +} + +#[test] +fn zips_two_lists() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [0 2 4 6 8] | zip [1 3 5 7 9] | flatten | into string | str collect '-' + "# + )); + + assert_eq!(actual.out, "0-1-2-3-4-5-6-7-8-9"); +} diff --git a/crates/nu-command/tests/format_conversions/bson.rs b/crates/nu-command/tests/format_conversions/bson.rs new file mode 100644 index 0000000000..cd7969e0f2 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/bson.rs @@ -0,0 +1,17 @@ +#[cfg(feature = "bson")] +#[test] +fn table_to_bson_and_back_into_table() { + use nu_test_support::{nu, pipeline}; + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.bson + | to bson + | from bson + | get root + | get 1.b + "# + )); + + assert_eq!(actual.out, "whel"); +} diff --git a/crates/nu-command/tests/format_conversions/csv.rs b/crates/nu-command/tests/format_conversions/csv.rs new file mode 100644 index 0000000000..4839e327ed --- /dev/null +++ b/crates/nu-command/tests/format_conversions/csv.rs @@ -0,0 +1,209 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn table_to_csv_text_and_from_csv_text_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open caco3_plastics.csv | to csv | from csv | first 1 | get origin " + ); + + assert_eq!(actual.out, "SPAIN"); +} + +#[test] +fn table_to_csv_text() { + Playground::setup("filter_to_csv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "csv_text_sample.txt", + r#" + importer,shipper,tariff_item,name,origin + Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain + Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open csv_text_sample.txt + | lines + | str trim + | split column "," a b c d origin + | last 1 + | to csv + | lines + | get 1 + "# + )); + + assert!(actual + .out + .contains("Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia")); + }) +} + +#[test] +fn table_to_csv_text_skipping_headers_after_conversion() { + Playground::setup("filter_to_csv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "csv_text_sample.txt", + r#" + importer,shipper,tariff_item,name,origin + Plasticos Rival,Reverte,2509000000,Calcium carbonate,Spain + Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open csv_text_sample.txt + | lines + | str trim + | split column "," a b c d origin + | last 1 + | to csv --noheaders + "# + )); + + assert!(actual + .out + .contains("Tigre Ecuador,OMYA Andina,3824909999,Calcium carbonate,Colombia")); + }) +} + +#[test] +fn infers_types() { + Playground::setup("filter_from_csv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_cuatro_mosqueteros.csv", + r#" + first_name,last_name,rusty_luck,d + Andrés,Robalino,1,d + Jonathan,Turner,1,d + Yehuda,Katz,1,d + Jason,Gedge,1,d + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_cuatro_mosqueteros.csv + | where rusty_luck > 0 + | length + "# + )); + + assert_eq!(actual.out, "4"); + }) +} + +#[test] +fn from_csv_text_to_table() { + Playground::setup("filter_from_csv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.txt", + r#" + first_name,last_name,rusty_luck + Andrés,Robalino,1 + Jonathan,Turner,1 + Yehuda,Katz,1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.txt + | from csv + | get rusty_luck + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn from_csv_text_with_separator_to_table() { + Playground::setup("filter_from_csv_test_3", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.txt", + r#" + first_name;last_name;rusty_luck + Andrés;Robalino;1 + Jonathan;Turner;1 + Yehuda;Katz;1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.txt + | from csv --separator ";" + | get rusty_luck + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn from_csv_text_with_tab_separator_to_table() { + Playground::setup("filter_from_csv_test_4", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_caballeros.txt", + r#" + first_name last_name rusty_luck + Andrés Robalino 1 + Jonathan Turner 1 + Yehuda Katz 1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_caballeros.txt + | from csv --separator (char tab) + | get rusty_luck + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn from_csv_text_skipping_headers_to_table() { + Playground::setup("filter_from_csv_test_5", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_amigos.txt", + r#" + Andrés,Robalino,1 + Jonathan,Turner,1 + Yehuda,Katz,1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_amigos.txt + | from csv --noheaders + | get Column3 + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} diff --git a/crates/nu-command/tests/format_conversions/eml.rs b/crates/nu-command/tests/format_conversions/eml.rs new file mode 100644 index 0000000000..41acb13cb9 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/eml.rs @@ -0,0 +1,93 @@ +use nu_test_support::{nu, pipeline}; + +const TEST_CWD: &str = "tests/fixtures/formats"; + +// The To field in this email is just "to@example.com", which gets parsed out as the Address. The Name is empty. +#[test] +fn from_eml_get_to_field() { + let actual = nu!( + cwd: TEST_CWD, + pipeline( + r#" + open sample.eml + | get To + | get Address + "# + ) + ); + + assert_eq!(actual.out, "to@example.com"); + + let actual = nu!( + cwd: TEST_CWD, + pipeline( + r#" + open sample.eml + | get To + | get Name + "# + ) + ); + + assert_eq!(actual.out, ""); +} + +// The Reply-To field in this email is "replyto@example.com" , meaning both the Name and Address values are identical. +#[test] +fn from_eml_get_replyto_field() { + let actual = nu!( + cwd: TEST_CWD, + pipeline( + r#" + open sample.eml + | get Reply-To + | get Address + "# + ) + ); + + assert_eq!(actual.out, "replyto@example.com"); + + let actual = nu!( + cwd: TEST_CWD, + pipeline( + r#" + open sample.eml + | get Reply-To + | get Name + "# + ) + ); + + assert_eq!(actual.out, "replyto@example.com"); +} + +#[test] +fn from_eml_get_subject_field() { + let actual = nu!( + cwd: TEST_CWD, + pipeline( + r#" + open sample.eml + | get Subject + "# + ) + ); + + assert_eq!(actual.out, "Test Message"); +} + +#[test] +fn from_eml_get_another_header_field() { + let actual = nu!( + cwd: TEST_CWD, + pipeline( + r#" + open sample.eml + | get MIME-Version + "# + ) + ); + + assert_eq!(actual.out, "1.0"); +} diff --git a/crates/nu-command/tests/format_conversions/html.rs b/crates/nu-command/tests/format_conversions/html.rs new file mode 100644 index 0000000000..555ac1abb3 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/html.rs @@ -0,0 +1,91 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn out_html_simple() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 3 | to html + "# + )); + + assert_eq!( + actual.out, + r"3" + ); +} + +#[test] +fn out_html_partial() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 3 | to html -p + "# + )); + + assert_eq!( + actual.out, + "
3
" + ); +} + +#[test] +fn out_html_table() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo '{"name": "darren"}' | from json | to html + "# + )); + + assert_eq!( + actual.out, + r"
name
darren
" + ); +} + +#[test] +fn test_cd_html_color_flag_dark_false() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cd --help | to html --html_color + "# + ) + ); + assert_eq!( + actual.out, + r"Usage:
> cd (path)

Flags:
-h, --help
Display this help message

Parameters:
(optional) path: the path to change to

" + ); +} + +#[test] +fn test_no_color_flag() { + let actual = nu!( + cwd: ".", pipeline( + r#" + cd --help | to html --no_color + "# + ) + ); + assert_eq!( + actual.out, + r"Usage:
> cd (path)

Flags:
-h, --help
Display this help message

Parameters:
(optional) path: the path to change to

" + ); +} + +#[test] +fn test_html_color_where_flag_dark_false() { + let actual = nu!( + cwd: ".", pipeline( + r#" + where --help | to html --html_color + "# + ) + ); + assert_eq!( + actual.out, + r"Usage:
> where <cond>

Flags:
-h, --help
Display this help message

Parameters:
cond: condition

" + ); +} diff --git a/crates/nu-command/tests/format_conversions/ics.rs b/crates/nu-command/tests/format_conversions/ics.rs new file mode 100644 index 0000000000..ccaa6318cf --- /dev/null +++ b/crates/nu-command/tests/format_conversions/ics.rs @@ -0,0 +1,99 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn infers_types() { + Playground::setup("filter_from_ics_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "calendar.ics", + r#" + BEGIN:VCALENDAR + PRODID:-//Google Inc//Google Calendar 70.9054//EN + VERSION:2.0 + BEGIN:VEVENT + DTSTART:20171007T200000Z + DTEND:20171007T233000Z + DTSTAMP:20200319T182138Z + UID:4l80f6dcovnriq38g57g07btid@google.com + CREATED:20170719T202915Z + DESCRIPTION: + LAST-MODIFIED:20170930T190808Z + LOCATION: + SEQUENCE:1 + STATUS:CONFIRMED + SUMMARY:Maryland Game + TRANSP:TRANSPARENT + END:VEVENT + BEGIN:VEVENT + DTSTART:20171002T010000Z + DTEND:20171002T020000Z + DTSTAMP:20200319T182138Z + UID:2v61g7mij4s7ieoubm3sjpun5d@google.com + CREATED:20171001T180103Z + DESCRIPTION: + LAST-MODIFIED:20171001T180103Z + LOCATION: + SEQUENCE:0 + STATUS:CONFIRMED + SUMMARY:Halloween Wars + TRANSP:OPAQUE + END:VEVENT + END:VCALENDAR + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open calendar.ics + | get events.0 + | length + "# + )); + + assert_eq!(actual.out, "2"); + }) +} + +#[test] +fn from_ics_text_to_table() { + Playground::setup("filter_from_ics_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "calendar.txt", + r#" + BEGIN:VCALENDAR + BEGIN:VEVENT + DTSTART:20171007T200000Z + DTEND:20171007T233000Z + DTSTAMP:20200319T182138Z + UID:4l80f6dcovnriq38g57g07btid@google.com + CREATED:20170719T202915Z + DESCRIPTION: + LAST-MODIFIED:20170930T190808Z + LOCATION: + SEQUENCE:1 + STATUS:CONFIRMED + SUMMARY:Maryland Game + TRANSP:TRANSPARENT + END:VEVENT + END:VCALENDAR + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open calendar.txt + | from ics + | get events.0 + | get properties.0 + | where name == "SUMMARY" + | first + | get value + "# + )); + + assert_eq!(actual.out, "Maryland Game"); + }) +} diff --git a/crates/nu-command/tests/format_conversions/json.rs b/crates/nu-command/tests/format_conversions/json.rs new file mode 100644 index 0000000000..1cb059c148 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/json.rs @@ -0,0 +1,100 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn table_to_json_text_and_from_json_text_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sgml_description.json + | to json + | from json + | get glossary.GlossDiv.GlossList.GlossEntry.GlossSee + "# + )); + + assert_eq!(actual.out, "markup"); +} + +#[test] +fn from_json_text_to_table() { + Playground::setup("filter_from_json_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "katz.txt", + r#" + { + "katz": [ + {"name": "Yehuda", "rusty_luck": 1}, + {"name": "Jonathan", "rusty_luck": 1}, + {"name": "Andres", "rusty_luck": 1}, + {"name":"GorbyPuff", "rusty_luck": 1} + ] + } + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), + "open katz.txt | from json | get katz | get rusty_luck | length " + ); + + assert_eq!(actual.out, "4"); + }) +} + +#[test] +fn from_json_text_recognizing_objects_independently_to_table() { + Playground::setup("filter_from_json_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "katz.txt", + r#" + {"name": "Yehuda", "rusty_luck": 1} + {"name": "Jonathan", "rusty_luck": 1} + {"name": "Andres", "rusty_luck": 1} + {"name":"GorbyPuff", "rusty_luck": 3} + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open katz.txt + | from json -o + | where name == "GorbyPuff" + | get rusty_luck + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn table_to_json_text() { + Playground::setup("filter_to_json_test", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "sample.txt", + r#" + JonAndrehudaTZ,3 + GorbyPuff,100 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open sample.txt + | lines + | split column "," name luck + | select name + | to json + | from json + | nth 0 + | get name + "# + )); + + assert_eq!(actual.out, "JonAndrehudaTZ"); + }) +} diff --git a/crates/nu-command/tests/format_conversions/markdown.rs b/crates/nu-command/tests/format_conversions/markdown.rs new file mode 100644 index 0000000000..15aef32756 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/markdown.rs @@ -0,0 +1,98 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn md_empty() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[]; []] | from json | to md + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn md_empty_pretty() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo "{}" | from json | to md -p + "# + )); + + assert_eq!(actual.out, ""); +} + +#[test] +fn md_simple() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 3 | to md + "# + )); + + assert_eq!(actual.out, "3"); +} + +#[test] +fn md_simple_pretty() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo 3 | to md -p + "# + )); + + assert_eq!(actual.out, "3"); +} + +#[test] +fn md_table() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[name]; [jason]] | to md + "# + )); + + assert_eq!(actual.out, "|name||-||jason|"); +} + +#[test] +fn md_table_pretty() { + let actual = nu!( + cwd: ".", pipeline( + r#" + echo [[name]; [joseph]] | to md -p + "# + )); + + assert_eq!(actual.out, "| name || ------ || joseph |"); +} + +#[test] +fn md_combined() { + let actual = nu!( + cwd: ".", pipeline( + r#" + def title [] { + echo [[H1]; ["Nu top meals"]] + }; + + def meals [] { + echo [[dish]; [Arepa] [Taco] [Pizza]] + }; + + title + | append (meals) + | to md --per-element --pretty + "# + )); + + assert_eq!( + actual.out, + "# Nu top meals| dish || ----- || Arepa || Taco || Pizza |" + ); +} diff --git a/crates/nu-command/tests/format_conversions/mod.rs b/crates/nu-command/tests/format_conversions/mod.rs new file mode 100644 index 0000000000..5af12f9fc1 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/mod.rs @@ -0,0 +1,17 @@ +mod bson; +mod csv; +mod eml; +mod html; +mod ics; +mod json; +mod markdown; +mod ods; +mod sqlite; +mod ssv; +mod toml; +mod tsv; +mod url; +mod vcf; +mod xlsx; +mod xml; +mod yaml; diff --git a/crates/nu-command/tests/format_conversions/ods.rs b/crates/nu-command/tests/format_conversions/ods.rs new file mode 100644 index 0000000000..c322e914e6 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/ods.rs @@ -0,0 +1,31 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn from_ods_file_to_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample_data.ods + | get SalesOrders + | nth 4 + | get Column2 + "# + )); + + assert_eq!(actual.out, "Gill"); +} + +#[test] +fn from_ods_file_to_table_select_sheet() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample_data.ods --raw + | from ods -s ["SalesOrders"] + | columns + | get 0 + "# + )); + + assert_eq!(actual.out, "SalesOrders"); +} diff --git a/crates/nu-command/tests/format_conversions/sqlite.rs b/crates/nu-command/tests/format_conversions/sqlite.rs new file mode 100644 index 0000000000..de6ce8ccfa --- /dev/null +++ b/crates/nu-command/tests/format_conversions/sqlite.rs @@ -0,0 +1,36 @@ +#[cfg(feature = "sqlite")] +use nu_test_support::{nu, pipeline}; + +#[cfg(feature = "sqlite")] +#[test] +fn table_to_sqlite_and_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | to sqlite + | from sqlite + | get table_values + | nth 2 + | get x + "# + )); + + assert_eq!(actual.out, "hello"); +} + +#[cfg(feature = "sqlite")] +#[test] +fn table_to_sqlite_and_back_into_table_select_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.db + | to sqlite + | from sqlite -t [strings] + | get table_names + "# + )); + + assert_eq!(actual.out, "strings"); +} diff --git a/crates/nu-command/tests/format_conversions/ssv.rs b/crates/nu-command/tests/format_conversions/ssv.rs new file mode 100644 index 0000000000..76d7045782 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/ssv.rs @@ -0,0 +1,95 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn from_ssv_text_to_table() { + Playground::setup("filter_from_ssv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.txt", + r#" + NAME LABELS SELECTOR IP PORT(S) + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open oc_get_svc.txt + | from ssv + | nth 0 + | get IP + "# + )); + + assert_eq!(actual.out, "172.30.78.158"); + }) +} + +#[test] +fn from_ssv_text_to_table_with_separator_specified() { + Playground::setup("filter_from_ssv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.txt", + r#" + NAME LABELS SELECTOR IP PORT(S) + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open oc_get_svc.txt + | from ssv --minimum-spaces 3 + | nth 0 + | get IP + "# + )); + + assert_eq!(actual.out, "172.30.78.158"); + }) +} + +#[test] +fn from_ssv_text_treating_first_line_as_data_with_flag() { + Playground::setup("filter_from_ssv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "oc_get_svc.txt", + r#" + docker-registry docker-registry=default docker-registry=default 172.30.78.158 5000/TCP + kubernetes component=apiserver,provider=kubernetes 172.30.0.2 443/TCP + kubernetes-ro component=apiserver,provider=kubernetes 172.30.0.1 80/TCP + "#, + )]); + + let aligned_columns = nu!( + cwd: dirs.test(), pipeline( + r#" + open oc_get_svc.txt + | from ssv --noheaders -a + | first + | get Column1 + "# + )); + + let separator_based = nu!( + cwd: dirs.test(), pipeline( + r#" + open oc_get_svc.txt + | from ssv --noheaders + | first + | get Column1 + + "# + )); + + assert_eq!(aligned_columns.out, separator_based.out); + assert_eq!(separator_based.out, "docker-registry"); + }) +} diff --git a/crates/nu-command/tests/format_conversions/toml.rs b/crates/nu-command/tests/format_conversions/toml.rs new file mode 100644 index 0000000000..cffeed1c24 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/toml.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn table_to_toml_text_and_from_toml_text_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open cargo_sample.toml + | to toml + | from toml + | get package.name + "# + )); + + assert_eq!(actual.out, "nu"); +} diff --git a/crates/nu-command/tests/format_conversions/tsv.rs b/crates/nu-command/tests/format_conversions/tsv.rs new file mode 100644 index 0000000000..066fa16ad3 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/tsv.rs @@ -0,0 +1,132 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn table_to_tsv_text_and_from_tsv_text_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", + "open caco3_plastics.tsv | to tsv | from tsv | first 1 | get origin" + ); + + assert_eq!(actual.out, "SPAIN"); +} + +#[test] +fn table_to_tsv_text_and_from_tsv_text_back_into_table_using_csv_separator() { + let actual = nu!( + cwd: "tests/fixtures/formats", + r"open caco3_plastics.tsv | to tsv | from csv --separator '\t' | first 1 | get origin" + ); + + assert_eq!(actual.out, "SPAIN"); +} + +#[test] +fn table_to_tsv_text() { + Playground::setup("filter_to_tsv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "tsv_text_sample.txt", + r#" + importer shipper tariff_item name origin + Plasticos Rival Reverte 2509000000 Calcium carbonate Spain + Tigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open tsv_text_sample.txt + | lines + | split column "\t" a b c d origin + | last 1 + | to tsv + | lines + | nth 1 + "# + )); + + assert!(actual.out.contains("Colombia")); + }) +} + +#[test] +fn table_to_tsv_text_skipping_headers_after_conversion() { + Playground::setup("filter_to_tsv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "tsv_text_sample.txt", + r#" + importer shipper tariff_item name origin + Plasticos Rival Reverte 2509000000 Calcium carbonate Spain + Tigre Ecuador OMYA Andina 3824909999 Calcium carbonate Colombia + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open tsv_text_sample.txt + | lines + | split column "\t" a b c d origin + | last 1 + | to tsv --noheaders + "# + )); + + assert!(actual.out.contains("Colombia")); + }) +} + +#[test] +fn from_tsv_text_to_table() { + Playground::setup("filter_from_tsv_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_amigos.txt", + r#" + first Name Last Name rusty_luck + Andrés Robalino 1 + Jonathan Turner 1 + Yehuda Katz 1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_amigos.txt + | from tsv + | get rusty_luck + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} + +#[test] +fn from_tsv_text_skipping_headers_to_table() { + Playground::setup("filter_from_tsv_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "los_tres_amigos.txt", + r#" + Andrés Robalino 1 + Jonathan Turner 1 + Yehuda Katz 1 + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open los_tres_amigos.txt + | from tsv --noheaders + | get Column3 + | length + "# + )); + + assert_eq!(actual.out, "3"); + }) +} diff --git a/crates/nu-command/tests/format_conversions/url.rs b/crates/nu-command/tests/format_conversions/url.rs new file mode 100644 index 0000000000..2187fa3822 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/url.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn can_encode_and_decode_urlencoding() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample.url + | to url + | from url + | get cheese + "# + )); + + assert_eq!(actual.out, "comté"); +} diff --git a/crates/nu-command/tests/format_conversions/vcf.rs b/crates/nu-command/tests/format_conversions/vcf.rs new file mode 100644 index 0000000000..ee4bf1bb52 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/vcf.rs @@ -0,0 +1,82 @@ +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; +use nu_test_support::{nu, pipeline}; + +#[test] +fn infers_types() { + Playground::setup("filter_from_vcf_test_1", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "contacts.vcf", + r#" + BEGIN:VCARD + VERSION:3.0 + FN:John Doe + N:Doe;John;;; + EMAIL;TYPE=INTERNET:john.doe99@gmail.com + item1.ORG:'Alpine Ski Resort' + item1.X-ABLabel:Other + item2.TITLE:'Ski Instructor' + item2.X-ABLabel:Other + BDAY:19001106 + NOTE:Facebook: john.doe.3\nWebsite: \nHometown: Cleveland\, Ohio + CATEGORIES:myContacts + END:VCARD + BEGIN:VCARD + VERSION:3.0 + FN:Alex Smith + N:Smith;Alex;;; + TEL;TYPE=CELL:(890) 123-4567 + CATEGORIES:Band,myContacts + END:VCARD + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open contacts.vcf + | length + "# + )); + + assert_eq!(actual.out, "2"); + }) +} + +#[test] +fn from_vcf_text_to_table() { + Playground::setup("filter_from_vcf_test_2", |dirs, sandbox| { + sandbox.with_files(vec![FileWithContentToBeTrimmed( + "contacts.txt", + r#" + BEGIN:VCARD + VERSION:3.0 + FN:John Doe + N:Doe;John;;; + EMAIL;TYPE=INTERNET:john.doe99@gmail.com + item1.ORG:'Alpine Ski Resort' + item1.X-ABLabel:Other + item2.TITLE:'Ski Instructor' + item2.X-ABLabel:Other + BDAY:19001106 + NOTE:Facebook: john.doe.3\nWebsite: \nHometown: Cleveland\, Ohio + CATEGORIES:myContacts + END:VCARD + "#, + )]); + + let actual = nu!( + cwd: dirs.test(), pipeline( + r#" + open contacts.txt + | from vcf + | get properties.0 + | where name == "EMAIL" + | first + | get value + "# + )); + + assert_eq!(actual.out, "john.doe99@gmail.com"); + }) +} diff --git a/crates/nu-command/tests/format_conversions/xlsx.rs b/crates/nu-command/tests/format_conversions/xlsx.rs new file mode 100644 index 0000000000..961c305cae --- /dev/null +++ b/crates/nu-command/tests/format_conversions/xlsx.rs @@ -0,0 +1,31 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn from_excel_file_to_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample_data.xlsx + | get SalesOrders + | nth 4 + | get Column2 + "# + )); + + assert_eq!(actual.out, "Gill"); +} + +#[test] +fn from_excel_file_to_table_select_sheet() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open sample_data.xlsx --raw + | from xlsx -s ["SalesOrders"] + | columns + | get 0 + "# + )); + + assert_eq!(actual.out, "SalesOrders"); +} diff --git a/crates/nu-command/tests/format_conversions/xml.rs b/crates/nu-command/tests/format_conversions/xml.rs new file mode 100644 index 0000000000..15a62d6907 --- /dev/null +++ b/crates/nu-command/tests/format_conversions/xml.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn table_to_xml_text_and_from_xml_text_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open jonathan.xml + | to xml + | from xml + | get rss.children.channel.children.0.3.item.children.guid.4.attributes.isPermaLink + "# + )); + + assert_eq!(actual.out, "true"); +} diff --git a/crates/nu-command/tests/format_conversions/yaml.rs b/crates/nu-command/tests/format_conversions/yaml.rs new file mode 100644 index 0000000000..887256637c --- /dev/null +++ b/crates/nu-command/tests/format_conversions/yaml.rs @@ -0,0 +1,16 @@ +use nu_test_support::{nu, pipeline}; + +#[test] +fn table_to_yaml_text_and_from_yaml_text_back_into_table() { + let actual = nu!( + cwd: "tests/fixtures/formats", pipeline( + r#" + open appveyor.yml + | to yaml + | from yaml + | get environment.global.PROJECT_NAME + "# + )); + + assert_eq!(actual.out, "nushell"); +} diff --git a/crates/nu-command/tests/main.rs b/crates/nu-command/tests/main.rs new file mode 100644 index 0000000000..62f9eb37d2 --- /dev/null +++ b/crates/nu-command/tests/main.rs @@ -0,0 +1,26 @@ +use nu_command::create_default_context; +use nu_protocol::engine::StateWorkingSet; +use quickcheck_macros::quickcheck; + +mod commands; +mod format_conversions; + +// use nu_engine::EvaluationContext; + +#[quickcheck] +fn quickcheck_parse(data: String) -> bool { + let (tokens, err) = nu_parser::lex(data.as_bytes(), 0, b"", b"", true); + let (lite_block, err2) = nu_parser::lite_parse(&tokens); + + if err.is_none() && err2.is_none() { + let cwd = std::env::current_dir().expect("Could not get current working directory."); + let context = create_default_context(cwd); + { + let mut working_set = StateWorkingSet::new(&context); + working_set.add_file("quickcheck".into(), data.as_bytes()); + + let _ = nu_parser::parse_block(&mut working_set, &lite_block, false); + } + } + true +} diff --git a/crates/nu-engine/src/documentation.rs b/crates/nu-engine/src/documentation.rs index 357b549c90..adadc7f1b3 100644 --- a/crates/nu-engine/src/documentation.rs +++ b/crates/nu-engine/src/documentation.rs @@ -230,7 +230,7 @@ pub fn get_documentation( match decl.run( engine_state, stack, - &Call::new(), + &Call::new(Span::new(0, 0)), Value::String { val: example.example.to_string(), span: Span { start: 0, end: 0 }, diff --git a/crates/nu-engine/src/eval.rs b/crates/nu-engine/src/eval.rs index d94f1b9ed8..6e7f037316 100644 --- a/crates/nu-engine/src/eval.rs +++ b/crates/nu-engine/src/eval.rs @@ -164,7 +164,7 @@ fn eval_external( let command = engine_state.get_decl(decl_id); - let mut call = Call::new(); + let mut call = Call::new(head.span); call.positional.push(head.clone()); @@ -484,7 +484,7 @@ pub fn eval_block( let table = engine_state.get_decl(decl_id).run( engine_state, stack, - &Call::new(), + &Call::new(Span::new(0, 0)), input, )?; diff --git a/crates/nu-parser/src/parser.rs b/crates/nu-parser/src/parser.rs index e9a710496d..3bc9340fbb 100644 --- a/crates/nu-parser/src/parser.rs +++ b/crates/nu-parser/src/parser.rs @@ -643,7 +643,7 @@ pub fn parse_internal_call( let mut error = None; - let mut call = Call::new(); + let mut call = Call::new(command_span); call.decl_id = decl_id; call.head = command_span; diff --git a/crates/nu-protocol/src/ast/call.rs b/crates/nu-protocol/src/ast/call.rs index 4586415577..365648a24e 100644 --- a/crates/nu-protocol/src/ast/call.rs +++ b/crates/nu-protocol/src/ast/call.rs @@ -10,17 +10,11 @@ pub struct Call { pub named: Vec<(Spanned, Option)>, } -impl Default for Call { - fn default() -> Self { - Self::new() - } -} - impl Call { - pub fn new() -> Call { + pub fn new(head: Span) -> Call { Self { decl_id: 0, - head: Span { start: 0, end: 0 }, + head, positional: vec![], named: vec![], } diff --git a/crates/nu-test-support/src/lib.rs b/crates/nu-test-support/src/lib.rs index b0f0685736..7075ae0cae 100644 --- a/crates/nu-test-support/src/lib.rs +++ b/crates/nu-test-support/src/lib.rs @@ -56,7 +56,7 @@ mod tests { open los_tres_amigos.txt | from-csv | get rusty_luck - | str to-int + | into int | math sum | echo "$it" "#, @@ -64,7 +64,7 @@ mod tests { assert_eq!( actual, - r#"open los_tres_amigos.txt | from-csv | get rusty_luck | str to-int | math sum | echo "$it""# + r#"open los_tres_amigos.txt | from-csv | get rusty_luck | into int | math sum | echo "$it""# ); } } diff --git a/crates/nu-test-support/src/playground/director.rs b/crates/nu-test-support/src/playground/director.rs index ba8b9922f4..de06bb3204 100644 --- a/crates/nu-test-support/src/playground/director.rs +++ b/crates/nu-test-support/src/playground/director.rs @@ -84,34 +84,32 @@ impl Director { 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 + let mut commands = String::new(); + if let Some(pipelines) = &self.pipeline { + for pipeline in pipelines { + if !commands.is_empty() { + commands.push_str("| "); + } + commands.push_str(&format!("{}\n", pipeline)); + } + } + + let process = match binary .construct() .stdout(Stdio::piped()) - .stdin(Stdio::piped()) + // .stdin(Stdio::piped()) .stderr(Stdio::piped()) + .arg(format!("-c '{}'", commands)) .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(|_| { diff --git a/crates/nu-test-support/src/playground/tests.rs b/crates/nu-test-support/src/playground/tests.rs index 014a86910e..cc4daa597b 100644 --- a/crates/nu-test-support/src/playground/tests.rs +++ b/crates/nu-test-support/src/playground/tests.rs @@ -1,23 +1,12 @@ 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| { diff --git a/src/tests/test_engine.rs b/src/tests/test_engine.rs index 6d330e54a5..a1df9f1641 100644 --- a/src/tests/test_engine.rs +++ b/src/tests/test_engine.rs @@ -72,7 +72,7 @@ fn in_variable_6() -> TestResult { #[test] fn help_works_with_missing_requirements() -> TestResult { - run_test(r#"each --help | lines | length"#, "17") + run_test(r#"each --help | lines | length"#, "16") } #[test] diff --git a/src/utils.rs b/src/utils.rs index 367dda38a2..82b2ef2bd2 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -209,10 +209,12 @@ fn print_pipeline_data( match engine_state.find_decl("table".as_bytes()) { Some(decl_id) => { - let table = - engine_state - .get_decl(decl_id) - .run(engine_state, stack, &Call::new(), input)?; + let table = engine_state.get_decl(decl_id).run( + engine_state, + stack, + &Call::new(Span::new(0, 0)), + input, + )?; for item in table { let stdout = std::io::stdout(); diff --git a/tests/shell/environment/configuration.rs b/tests/shell/environment/configuration.rs deleted file mode 100644 index d44dd7e750..0000000000 --- a/tests/shell/environment/configuration.rs +++ /dev/null @@ -1,142 +0,0 @@ -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/in_sync.rs b/tests/shell/environment/in_sync.rs deleted file mode 100644 index 353507644e..0000000000 --- a/tests/shell/environment/in_sync.rs +++ /dev/null @@ -1,116 +0,0 @@ -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 index f505dcbf35..e35eff939e 100644 --- a/tests/shell/environment/mod.rs +++ b/tests/shell/environment/mod.rs @@ -1,6 +1,4 @@ -mod configuration; mod env; -mod in_sync; mod nu_env; pub mod support { diff --git a/tests/shell/mod.rs b/tests/shell/mod.rs index 24b62ccd09..0fff95de05 100644 --- a/tests/shell/mod.rs +++ b/tests/shell/mod.rs @@ -1,28 +1,10 @@ -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]