mirror of
https://github.com/nushell/nushell
synced 2024-12-31 23:39:00 +00:00
253b223e65
Fixes #7615 # Description When calling external commands, we create a table from the pipeline data to handle external commands expecting paginated input. When a binary value is made into a table, we convert the vector of bytes representing the binary bytes into a pretty formatted string. This results in the pretty formatted string being sent to external commands instead of the actual binary bytes. By checking whether the stdout of the call is being redirected, we can decide whether to send the raw binary bytes or the pretty formatted output when creating a table command. # User-Facing Changes When passing binary values to external commands, the external command will receive the actual bytes instead of the pretty printed string. Use cases that don't involve piping a binary value into an external command are unchanged. ![new_behavior](https://user-images.githubusercontent.com/32406734/218349172-24cd12f2-d563-4957-bdf1-6aa804b174b2.png) # Tests + Formatting Don't forget to add tests that cover your changes. Make sure you've run and fixed any issues with these commands: cargo fmt --all -- --check to check standard code formatting (cargo fmt --all applies these changes) cargo clippy --workspace -- -D warnings -D clippy::unwrap_used -A clippy::needless_collect to check that you're using the standard code style cargo test --workspace to check that all tests pass # After Submitting If your PR had any user-facing changes, update [the documentation](https://github.com/nushell/nushell.github.io) after the PR is merged, if necessary. This will help us keep the docs up to date.
311 lines
7.9 KiB
Rust
311 lines
7.9 KiB
Rust
#[cfg(not(windows))]
|
|
use nu_test_support::fs::Stub::EmptyFile;
|
|
use nu_test_support::playground::Playground;
|
|
use nu_test_support::{nu, pipeline};
|
|
|
|
#[test]
|
|
fn better_empty_redirection() {
|
|
let actual = nu!(
|
|
cwd: "tests/fixtures/formats", pipeline(
|
|
r#"
|
|
ls | each { |it| nu --testbin cococo $it.name } | ignore
|
|
"#
|
|
));
|
|
|
|
eprintln!("out: {}", actual.out);
|
|
|
|
assert!(!actual.out.contains('2'));
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn explicit_glob() {
|
|
Playground::setup("external with explicit glob", |dirs, sandbox| {
|
|
sandbox.with_files(vec![
|
|
EmptyFile("D&D_volume_1.txt"),
|
|
EmptyFile("D&D_volume_2.txt"),
|
|
EmptyFile("foo.sh"),
|
|
]);
|
|
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^ls | glob '*.txt' | length
|
|
"#
|
|
));
|
|
|
|
assert_eq!(actual.out, "2");
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn bare_word_expand_path_glob() {
|
|
Playground::setup("bare word should do the expansion", |dirs, sandbox| {
|
|
sandbox.with_files(vec![
|
|
EmptyFile("D&D_volume_1.txt"),
|
|
EmptyFile("D&D_volume_2.txt"),
|
|
EmptyFile("foo.sh"),
|
|
]);
|
|
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^ls *.txt
|
|
"#
|
|
));
|
|
|
|
assert!(actual.out.contains("D&D_volume_1.txt"));
|
|
assert!(actual.out.contains("D&D_volume_2.txt"));
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn backtick_expand_path_glob() {
|
|
Playground::setup("backtick should do the expansion", |dirs, sandbox| {
|
|
sandbox.with_files(vec![
|
|
EmptyFile("D&D_volume_1.txt"),
|
|
EmptyFile("D&D_volume_2.txt"),
|
|
EmptyFile("foo.sh"),
|
|
]);
|
|
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^ls `*.txt`
|
|
"#
|
|
));
|
|
|
|
assert!(actual.out.contains("D&D_volume_1.txt"));
|
|
assert!(actual.out.contains("D&D_volume_2.txt"));
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn single_quote_does_not_expand_path_glob() {
|
|
Playground::setup("single quote do not run the expansion", |dirs, sandbox| {
|
|
sandbox.with_files(vec![
|
|
EmptyFile("D&D_volume_1.txt"),
|
|
EmptyFile("D&D_volume_2.txt"),
|
|
EmptyFile("foo.sh"),
|
|
]);
|
|
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^ls '*.txt'
|
|
"#
|
|
));
|
|
|
|
assert!(actual.err.contains("No such file or directory"));
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn double_quote_does_not_expand_path_glob() {
|
|
Playground::setup("double quote do not run the expansion", |dirs, sandbox| {
|
|
sandbox.with_files(vec![
|
|
EmptyFile("D&D_volume_1.txt"),
|
|
EmptyFile("D&D_volume_2.txt"),
|
|
EmptyFile("foo.sh"),
|
|
]);
|
|
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^ls "*.txt"
|
|
"#
|
|
));
|
|
|
|
assert!(actual.err.contains("No such file or directory"));
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn failed_command_with_semicolon_will_not_execute_following_cmds() {
|
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^ls *.abc; echo done
|
|
"#
|
|
));
|
|
|
|
assert!(!actual.out.contains("done"));
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn external_args_with_quoted() {
|
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^echo "foo=bar 'hi'"
|
|
"#
|
|
));
|
|
|
|
assert_eq!(actual.out, "foo=bar 'hi'");
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn external_arg_with_long_flag_value_quoted() {
|
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^echo --foo='bar'
|
|
"#
|
|
));
|
|
|
|
assert_eq!(actual.out, "--foo=bar");
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn external_arg_with_variable_name() {
|
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
let dump_command = "PGPASSWORD='db_secret' pg_dump -Fc -h 'db.host' -p '$db.port' -U postgres -d 'db_name' > '/tmp/dump_name'";
|
|
nu --testbin nonu $dump_command
|
|
"#
|
|
));
|
|
|
|
assert_eq!(
|
|
actual.out,
|
|
r#"PGPASSWORD='db_secret' pg_dump -Fc -h 'db.host' -p '$db.port' -U postgres -d 'db_name' > '/tmp/dump_name'"#
|
|
);
|
|
})
|
|
}
|
|
|
|
#[cfg(not(windows))]
|
|
#[test]
|
|
fn external_command_escape_args() {
|
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^echo "\"abcd"
|
|
"#
|
|
));
|
|
|
|
assert_eq!(actual.out, r#""abcd"#);
|
|
})
|
|
}
|
|
|
|
#[test]
|
|
fn external_command_not_expand_tilde_with_quotes() {
|
|
Playground::setup(
|
|
"external command not expand tilde with quotes",
|
|
|dirs, _| {
|
|
let actual = nu!(cwd: dirs.test(), pipeline(r#"nu --testbin nonu "~""#));
|
|
assert_eq!(actual.out, r#"~"#);
|
|
},
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn external_command_receives_raw_binary_data() {
|
|
Playground::setup("external command receives raw binary data", |dirs, _| {
|
|
let actual =
|
|
nu!(cwd: dirs.test(), pipeline(r#"0x[deadbeef] | nu --testbin input_bytes_length"#));
|
|
assert_eq!(actual.out, r#"4"#);
|
|
})
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn failed_command_with_semicolon_will_not_execute_following_cmds_windows() {
|
|
Playground::setup("external failed command with semicolon", |dirs, _| {
|
|
let actual = nu!(
|
|
cwd: dirs.test(), pipeline(
|
|
r#"
|
|
^cargo asdf; echo done
|
|
"#
|
|
));
|
|
|
|
assert!(!actual.out.contains("done"));
|
|
})
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn can_run_batch_files() {
|
|
use nu_test_support::fs::Stub::FileWithContent;
|
|
Playground::setup("run a Windows batch file", |dirs, sandbox| {
|
|
sandbox.with_files(vec![FileWithContent(
|
|
"foo.cmd",
|
|
r#"
|
|
@echo off
|
|
echo Hello World
|
|
"#,
|
|
)]);
|
|
|
|
let actual = nu!(cwd: dirs.test(), pipeline("foo.cmd"));
|
|
assert!(actual.out.contains("Hello World"));
|
|
});
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn can_run_batch_files_without_cmd_extension() {
|
|
use nu_test_support::fs::Stub::FileWithContent;
|
|
Playground::setup(
|
|
"run a Windows batch file without specifying the extension",
|
|
|dirs, sandbox| {
|
|
sandbox.with_files(vec![FileWithContent(
|
|
"foo.cmd",
|
|
r#"
|
|
@echo off
|
|
echo Hello World
|
|
"#,
|
|
)]);
|
|
|
|
let actual = nu!(cwd: dirs.test(), pipeline("foo"));
|
|
assert!(actual.out.contains("Hello World"));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn can_run_batch_files_without_bat_extension() {
|
|
use nu_test_support::fs::Stub::FileWithContent;
|
|
Playground::setup(
|
|
"run a Windows batch file without specifying the extension",
|
|
|dirs, sandbox| {
|
|
sandbox.with_files(vec![FileWithContent(
|
|
"foo.bat",
|
|
r#"
|
|
@echo off
|
|
echo Hello World
|
|
"#,
|
|
)]);
|
|
|
|
let actual = nu!(cwd: dirs.test(), pipeline("foo"));
|
|
assert!(actual.out.contains("Hello World"));
|
|
},
|
|
);
|
|
}
|
|
|
|
#[cfg(windows)]
|
|
#[test]
|
|
fn quotes_trimmed_when_shelling_out() {
|
|
// regression test for a bug where we weren't trimming quotes around string args before shelling out to cmd.exe
|
|
let actual = nu!(cwd: ".", pipeline(
|
|
r#"
|
|
^echo "foo"
|
|
"#
|
|
));
|
|
|
|
assert_eq!(actual.out, "foo");
|
|
}
|