forked from extern/nushell
Simplify nu!
test macros. (#10403)
# Description Unify the logic between `nu!` and `nu_with_std!`. The inner code actually does not contain any variadic components. So it can safely be abstracted into a function. Similarly simplify the variadic to an array in `nu_with_plugin!` This also seems to simplify the codegen for tests. Comparing the size of the `/target/debug` folder after running: ```sh cargo clean --profile dev cargo build --workspace --tests ``` With this branch a reduction from `8.9GB` to `8.7GB` # User-Facing Changes None # Tests + Formatting No changes necessary
This commit is contained in:
parent
0c7a8e3634
commit
4aa9102523
@ -96,7 +96,7 @@ macro_rules! nu {
|
||||
|
||||
// Create the NuOpts struct from the `field => value ;` pairs
|
||||
(@nu_opts $( $field:ident => $value:expr ; )*) => {
|
||||
NuOpts{
|
||||
$crate::macros::NuOpts{
|
||||
$(
|
||||
$field: Some($value),
|
||||
)*
|
||||
@ -115,97 +115,11 @@ macro_rules! nu {
|
||||
|
||||
// Do the actual work.
|
||||
(@main $opts:expr, $path:expr) => {{
|
||||
pub use std::error::Error;
|
||||
pub use std::io::prelude::*;
|
||||
pub use std::process::{Command, Stdio};
|
||||
pub use $crate::NATIVE_PATH_ENV_VAR;
|
||||
|
||||
pub fn escape_quote_string(input: String) -> String {
|
||||
let mut output = String::with_capacity(input.len() + 2);
|
||||
output.push('"');
|
||||
|
||||
for c in input.chars() {
|
||||
if c == '"' || c == '\\' {
|
||||
output.push('\\');
|
||||
}
|
||||
output.push(c);
|
||||
}
|
||||
|
||||
output.push('"');
|
||||
output
|
||||
}
|
||||
|
||||
let test_bins = $crate::fs::binaries();
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
let test_bins = $crate::nu_path::canonicalize_with(&test_bins, cwd).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||
test_bins.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let mut paths = $crate::shell_os_paths();
|
||||
paths.insert(0, test_bins);
|
||||
|
||||
let path = $path.lines().collect::<Vec<_>>().join("; ");
|
||||
|
||||
let paths_joined = match std::env::join_paths(paths) {
|
||||
Ok(all) => all,
|
||||
Err(_) => panic!("Couldn't join paths for PATH var."),
|
||||
};
|
||||
|
||||
let target_cwd = $opts.cwd.unwrap_or(".".to_string());
|
||||
let locale = $opts.locale.unwrap_or("en_US.UTF-8".to_string());
|
||||
let executable_path = $crate::fs::executable_path();
|
||||
|
||||
let mut command = $crate::macros::run_command(&executable_path, &target_cwd);
|
||||
command
|
||||
.env(nu_utils::locale::LOCALE_OVERRIDE_ENV_VAR, locale)
|
||||
.env(NATIVE_PATH_ENV_VAR, paths_joined)
|
||||
// TODO: consider adding custom plugin path for tests to
|
||||
// not interfere with user local environment
|
||||
// .arg("--skip-plugins")
|
||||
// .arg("--no-history")
|
||||
// .arg("--config-file")
|
||||
// .arg($crate::fs::DisplayPath::display_path(&$crate::fs::fixtures().join("playground/config/default.toml")))
|
||||
.arg("--no-std-lib")
|
||||
.arg(format!("-c {}", escape_quote_string(path)))
|
||||
.stdout(Stdio::piped())
|
||||
// .stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
let mut process = match command.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {:?} {}", $crate::fs::executable_path(), why.to_string()),
|
||||
};
|
||||
|
||||
// let stdin = process.stdin.as_mut().expect("couldn't open stdin");
|
||||
// stdin
|
||||
// .write_all(b"exit\n")
|
||||
// .expect("couldn't write to stdin");
|
||||
|
||||
let output = process
|
||||
.wait_with_output()
|
||||
.expect("couldn't read from stdout/stderr");
|
||||
|
||||
let out = $crate::macros::read_std(&output.stdout);
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
println!("=== stderr\n{}", err);
|
||||
|
||||
$crate::Outcome::new(out,err.into_owned())
|
||||
$crate::macros::nu_run_test($opts, $path, false)
|
||||
}};
|
||||
|
||||
// This is the entrypoint for this macro.
|
||||
($($token:tt)*) => {{
|
||||
#[derive(Default)]
|
||||
struct NuOpts {
|
||||
cwd: Option<String>,
|
||||
locale: Option<String>,
|
||||
}
|
||||
|
||||
nu!(@options [ ] $($token)*)
|
||||
}};
|
||||
@ -258,7 +172,7 @@ macro_rules! nu_with_std {
|
||||
|
||||
// Create the NuOpts struct from the `field => value ;` pairs
|
||||
(@nu_opts $( $field:ident => $value:expr ; )*) => {
|
||||
NuOpts{
|
||||
$crate::macros::NuOpts{
|
||||
$(
|
||||
$field: Some($value),
|
||||
)*
|
||||
@ -277,201 +191,190 @@ macro_rules! nu_with_std {
|
||||
|
||||
// Do the actual work.
|
||||
(@main $opts:expr, $path:expr) => {{
|
||||
pub use std::error::Error;
|
||||
pub use std::io::prelude::*;
|
||||
pub use std::process::Stdio;
|
||||
pub use $crate::NATIVE_PATH_ENV_VAR;
|
||||
|
||||
pub fn escape_quote_string(input: String) -> String {
|
||||
let mut output = String::with_capacity(input.len() + 2);
|
||||
output.push('"');
|
||||
|
||||
for c in input.chars() {
|
||||
if c == '"' || c == '\\' {
|
||||
output.push('\\');
|
||||
}
|
||||
output.push(c);
|
||||
}
|
||||
|
||||
output.push('"');
|
||||
output
|
||||
}
|
||||
|
||||
let test_bins = $crate::fs::binaries();
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
let test_bins = $crate::nu_path::canonicalize_with(&test_bins, cwd).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||
test_bins.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let mut paths = $crate::shell_os_paths();
|
||||
paths.insert(0, test_bins);
|
||||
|
||||
let path = $path.lines().collect::<Vec<_>>().join("; ");
|
||||
|
||||
let paths_joined = match std::env::join_paths(paths) {
|
||||
Ok(all) => all,
|
||||
Err(_) => panic!("Couldn't join paths for PATH var."),
|
||||
};
|
||||
|
||||
let target_cwd = $opts.cwd.unwrap_or(".".to_string());
|
||||
let locale = $opts.locale.unwrap_or("en_US.UTF-8".to_string());
|
||||
let executable_path = $crate::fs::executable_path();
|
||||
|
||||
let mut command = $crate::macros::run_command(&executable_path, &target_cwd);
|
||||
command
|
||||
.env(nu_utils::locale::LOCALE_OVERRIDE_ENV_VAR, locale)
|
||||
.env(NATIVE_PATH_ENV_VAR, paths_joined)
|
||||
// .arg("--skip-plugins")
|
||||
// .arg("--no-history")
|
||||
// .arg("--config-file")
|
||||
// .arg($crate::fs::DisplayPath::display_path(&$crate::fs::fixtures().join("playground/config/default.toml")))
|
||||
.arg(format!("-c {}", escape_quote_string(path)))
|
||||
.stdout(Stdio::piped())
|
||||
// .stdin(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
let mut process = match command.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {:?} {}", $crate::fs::executable_path(), why.to_string()),
|
||||
};
|
||||
|
||||
// let stdin = process.stdin.as_mut().expect("couldn't open stdin");
|
||||
// stdin
|
||||
// .write_all(b"exit\n")
|
||||
// .expect("couldn't write to stdin");
|
||||
|
||||
let output = process
|
||||
.wait_with_output()
|
||||
.expect("couldn't read from stdout/stderr");
|
||||
|
||||
let out = $crate::macros::read_std(&output.stdout);
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
println!("=== stderr\n{}", err);
|
||||
|
||||
$crate::Outcome::new(out,err.into_owned())
|
||||
$crate::macros::nu_run_test($opts, $path, true)
|
||||
}};
|
||||
|
||||
// This is the entrypoint for this macro.
|
||||
($($token:tt)*) => {{
|
||||
#[derive(Default)]
|
||||
struct NuOpts {
|
||||
cwd: Option<String>,
|
||||
locale: Option<String>,
|
||||
}
|
||||
|
||||
nu!(@options [ ] $($token)*)
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! with_exe {
|
||||
($name:literal) => {{
|
||||
#[cfg(windows)]
|
||||
{
|
||||
concat!($name, ".exe")
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
$name
|
||||
}
|
||||
}};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! nu_with_plugins {
|
||||
(cwd: $cwd:expr, plugins: [$(($plugin_name:expr)),+$(,)?], $command:expr) => {{
|
||||
nu_with_plugins!($cwd, [$(("", $plugin_name)),+], $command)
|
||||
$crate::macros::nu_with_plugin_run_test($cwd, &[$($plugin_name),+], $command)
|
||||
}};
|
||||
(cwd: $cwd:expr, plugin: ($plugin_name:expr), $command:expr) => {{
|
||||
nu_with_plugins!($cwd, [("", $plugin_name)], $command)
|
||||
$crate::macros::nu_with_plugin_run_test($cwd, &[$plugin_name], $command)
|
||||
}};
|
||||
|
||||
($cwd:expr, [$(($format:expr, $plugin_name:expr)),+$(,)?], $command:expr) => {{
|
||||
pub use std::error::Error;
|
||||
pub use std::io::prelude::*;
|
||||
pub use std::process::Stdio;
|
||||
pub use tempfile::tempdir;
|
||||
pub use $crate::{NATIVE_PATH_ENV_VAR, with_exe};
|
||||
|
||||
let test_bins = $crate::fs::binaries();
|
||||
let test_bins = nu_path::canonicalize_with(&test_bins, ".").unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||
test_bins.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let temp = tempdir().expect("couldn't create a temporary directory");
|
||||
let temp_plugin_file = temp.path().join("plugin.nu");
|
||||
std::fs::File::create(&temp_plugin_file).expect("couldn't create temporary plugin file");
|
||||
|
||||
$crate::commands::ensure_plugins_built();
|
||||
|
||||
// TODO: the `$format` is a dummy empty string, but `plugin_name` is repeatable
|
||||
// just keep it here for now. Need to find a way to remove it.
|
||||
let registrations = format!(
|
||||
concat!($(concat!("register ", $format, " {};")),+),
|
||||
$(
|
||||
nu_path::canonicalize_with(with_exe!($plugin_name), &test_bins)
|
||||
.unwrap_or_else(|e| {
|
||||
panic!("failed to canonicalize plugin {} path", $plugin_name)
|
||||
})
|
||||
.display()
|
||||
),+
|
||||
);
|
||||
let commands = format!("{registrations}{}", $command);
|
||||
|
||||
let target_cwd = $crate::fs::in_directory(&$cwd);
|
||||
// In plugin testing, we need to use installed nushell to drive
|
||||
// plugin commands.
|
||||
let mut executable_path = $crate::fs::executable_path();
|
||||
if !executable_path.exists() {
|
||||
executable_path = $crate::fs::installed_nu_path();
|
||||
}
|
||||
let mut process = match $crate::macros::run_command(&executable_path, &target_cwd)
|
||||
.arg("--commands")
|
||||
.arg(commands)
|
||||
.arg("--plugin-config")
|
||||
.arg(temp_plugin_file)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {}", why.to_string()),
|
||||
};
|
||||
|
||||
let output = process
|
||||
.wait_with_output()
|
||||
.expect("couldn't read from stdout/stderr");
|
||||
|
||||
let out = $crate::macros::read_std(&output.stdout);
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
println!("=== stderr\n{}", err);
|
||||
|
||||
$crate::Outcome::new(out, err.into_owned())
|
||||
}};
|
||||
}
|
||||
|
||||
pub fn read_std(std: &[u8]) -> String {
|
||||
use crate::{Outcome, NATIVE_PATH_ENV_VAR};
|
||||
use std::{
|
||||
path::Path,
|
||||
process::{Command, Stdio},
|
||||
};
|
||||
use tempfile::tempdir;
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct NuOpts {
|
||||
pub cwd: Option<String>,
|
||||
pub locale: Option<String>,
|
||||
}
|
||||
|
||||
pub fn nu_run_test(opts: NuOpts, commands: impl AsRef<str>, with_std: bool) -> Outcome {
|
||||
let test_bins = crate::fs::binaries();
|
||||
|
||||
let cwd = std::env::current_dir().expect("Could not get current working directory.");
|
||||
let test_bins = nu_path::canonicalize_with(&test_bins, cwd).unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||
test_bins.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let mut paths = crate::shell_os_paths();
|
||||
paths.insert(0, test_bins);
|
||||
|
||||
let commands = commands.as_ref().lines().collect::<Vec<_>>().join("; ");
|
||||
|
||||
let paths_joined = match std::env::join_paths(paths) {
|
||||
Ok(all) => all,
|
||||
Err(_) => panic!("Couldn't join paths for PATH var."),
|
||||
};
|
||||
|
||||
let target_cwd = opts.cwd.unwrap_or(".".to_string());
|
||||
let locale = opts.locale.unwrap_or("en_US.UTF-8".to_string());
|
||||
let executable_path = crate::fs::executable_path();
|
||||
|
||||
let mut command = setup_command(&executable_path, &target_cwd);
|
||||
command
|
||||
.env(nu_utils::locale::LOCALE_OVERRIDE_ENV_VAR, locale)
|
||||
.env(NATIVE_PATH_ENV_VAR, paths_joined);
|
||||
// TODO: consider adding custom plugin path for tests to
|
||||
// not interfere with user local environment
|
||||
if !with_std {
|
||||
command.arg("--no-std-lib");
|
||||
}
|
||||
command
|
||||
.arg(format!("-c {}", escape_quote_string(commands)))
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped());
|
||||
|
||||
let process = match command.spawn() {
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {:?} {}", crate::fs::executable_path(), why),
|
||||
};
|
||||
|
||||
let output = process
|
||||
.wait_with_output()
|
||||
.expect("couldn't read from stdout/stderr");
|
||||
|
||||
let out = collapse_output(&output.stdout);
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
println!("=== stderr\n{}", err);
|
||||
|
||||
Outcome::new(out, err.into_owned())
|
||||
}
|
||||
|
||||
pub fn nu_with_plugin_run_test(cwd: impl AsRef<Path>, plugins: &[&str], command: &str) -> Outcome {
|
||||
let test_bins = crate::fs::binaries();
|
||||
let test_bins = nu_path::canonicalize_with(&test_bins, ".").unwrap_or_else(|e| {
|
||||
panic!(
|
||||
"Couldn't canonicalize dummy binaries path {}: {:?}",
|
||||
test_bins.display(),
|
||||
e
|
||||
)
|
||||
});
|
||||
|
||||
let temp = tempdir().expect("couldn't create a temporary directory");
|
||||
let temp_plugin_file = temp.path().join("plugin.nu");
|
||||
std::fs::File::create(&temp_plugin_file).expect("couldn't create temporary plugin file");
|
||||
|
||||
crate::commands::ensure_plugins_built();
|
||||
|
||||
let registrations: String = plugins
|
||||
.iter()
|
||||
.map(|plugin_name| {
|
||||
let plugin = with_exe(plugin_name);
|
||||
let plugin_path = nu_path::canonicalize_with(&plugin, &test_bins)
|
||||
.unwrap_or_else(|_| panic!("failed to canonicalize plugin {} path", &plugin));
|
||||
let plugin_path = plugin_path.to_string_lossy();
|
||||
format!("register {plugin_path};")
|
||||
})
|
||||
.collect();
|
||||
let commands = format!("{registrations}{command}");
|
||||
|
||||
let target_cwd = crate::fs::in_directory(&cwd);
|
||||
// In plugin testing, we need to use installed nushell to drive
|
||||
// plugin commands.
|
||||
let mut executable_path = crate::fs::executable_path();
|
||||
if !executable_path.exists() {
|
||||
executable_path = crate::fs::installed_nu_path();
|
||||
}
|
||||
let process = match setup_command(&executable_path, &target_cwd)
|
||||
.arg("--commands")
|
||||
.arg(commands)
|
||||
.arg("--plugin-config")
|
||||
.arg(temp_plugin_file)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::piped())
|
||||
.spawn()
|
||||
{
|
||||
Ok(child) => child,
|
||||
Err(why) => panic!("Can't run test {}", why),
|
||||
};
|
||||
|
||||
let output = process
|
||||
.wait_with_output()
|
||||
.expect("couldn't read from stdout/stderr");
|
||||
|
||||
let out = collapse_output(&output.stdout);
|
||||
let err = String::from_utf8_lossy(&output.stderr);
|
||||
|
||||
println!("=== stderr\n{}", err);
|
||||
|
||||
Outcome::new(out, err.into_owned())
|
||||
}
|
||||
|
||||
fn escape_quote_string(input: String) -> String {
|
||||
let mut output = String::with_capacity(input.len() + 2);
|
||||
output.push('"');
|
||||
|
||||
for c in input.chars() {
|
||||
if c == '"' || c == '\\' {
|
||||
output.push('\\');
|
||||
}
|
||||
output.push(c);
|
||||
}
|
||||
|
||||
output.push('"');
|
||||
output
|
||||
}
|
||||
|
||||
fn with_exe(name: &str) -> String {
|
||||
#[cfg(windows)]
|
||||
{
|
||||
name.to_string() + ".exe"
|
||||
}
|
||||
#[cfg(not(windows))]
|
||||
{
|
||||
name.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
fn collapse_output(std: &[u8]) -> String {
|
||||
let out = String::from_utf8_lossy(std);
|
||||
let out = out.lines().collect::<Vec<_>>().join("\n");
|
||||
let out = out.replace("\r\n", "");
|
||||
out.replace('\n', "")
|
||||
}
|
||||
|
||||
use std::{path::Path, process::Command};
|
||||
|
||||
pub fn run_command(executable_path: &Path, target_cwd: &str) -> Command {
|
||||
fn setup_command(executable_path: &Path, target_cwd: &str) -> Command {
|
||||
let mut command = Command::new(executable_path);
|
||||
|
||||
command
|
||||
|
Loading…
Reference in New Issue
Block a user