Playground infraestructure (tests, etc) additions. (#3179)

* Playground infraestructure (tests, etc) additions.

A few things to note:

* Nu can be started with a custom configuration file (`nu --config-file /path/to/sample_config.toml`). Useful for mocking the configuration on test runs.
* When given a custom configuration file Nu will save any changes to the file supplied appropiately.
* The `$nu.config-path` variable either shows the default configuration file (or the custom one, if given)
* We can now run end to end tests with finer grained control (currently, since this is baseline work, standard out) This will allow to check things like exit status, assert the contents with a format, etc)

* Remove (for another PR)
This commit is contained in:
Andrés N. Robalino
2021-03-15 02:26:30 -05:00
committed by GitHub
parent 82b6300dcb
commit d2213d18fa
41 changed files with 1128 additions and 408 deletions

View File

@ -124,6 +124,7 @@ shadow-rs = "0.5"
[dev-dependencies]
quickcheck = "1.0.3"
quickcheck_macros = "1.0.0"
hamcrest2 = "0.3.0"
[features]
clipboard-cli = ["arboard"]

View File

@ -152,7 +152,7 @@ pub(crate) use char_::Char;
pub(crate) use chart::Chart;
pub(crate) use compact::Compact;
pub(crate) use config::{
Config, ConfigClear, ConfigGet, ConfigLoad, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
Config, ConfigClear, ConfigGet, ConfigPath, ConfigRemove, ConfigSet, ConfigSetInto,
};
pub(crate) use cp::Cpy;
pub(crate) use date::{Date, DateFormat, DateListTimeZone, DateNow, DateToTable, DateToTimeZone};

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
pub struct SubCommand;
@ -35,13 +35,19 @@ impl WholeStreamCommand for SubCommand {
pub async fn clear(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = nu_data::config::read(name_span, &None)?;
let path = match args.scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let mut result = nu_data::config::read(name_span, &path)?;
result.clear();
config::write(&result, &None)?;
config::write(&result, &path)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(args.call_info.name_tag),

View File

@ -1,13 +1,15 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct SubCommand;
#[derive(Deserialize)]
pub struct GetArgs {
path: ColumnPath,
pub struct Arguments {
column_path: ColumnPath,
}
#[async_trait]
@ -42,14 +44,21 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn get(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (GetArgs { path }, _) = args.process().await?;
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { column_path }, _) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let result = UntaggedValue::row(nu_data::config::read(&name_tag, &None)?).into_value(&name_tag);
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let value = crate::commands::get::get_column_path(&path, &result)?;
let result = UntaggedValue::row(nu_data::config::read(&name, &path)?).into_value(&name);
let value = crate::commands::get::get_column_path(&column_path, &result)?;
Ok(match value {
Value {

View File

@ -1,51 +0,0 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_source::Tagged;
use std::path::PathBuf;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct LoadArgs {
load: Tagged<PathBuf>,
}
#[async_trait]
impl WholeStreamCommand for SubCommand {
fn name(&self) -> &str {
"config load"
}
fn signature(&self) -> Signature {
Signature::build("config load").required(
"load",
SyntaxShape::FilePath,
"Path to load the config from",
)
}
fn usage(&self) -> &str {
"Loads the config from the path given"
}
async fn run(&self, args: CommandArgs) -> Result<OutputStream, ShellError> {
set(args).await
}
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name = args.call_info.name_tag.clone();
let name_span = args.call_info.name_tag.clone();
let (LoadArgs { load }, _) = args.process().await?;
let configuration = load.item().clone();
let result = nu_data::config::read(name_span, &Some(configuration))?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
)])
.to_output_stream())
}

View File

@ -1,7 +1,6 @@
pub mod clear;
pub mod command;
pub mod get;
pub mod load;
pub mod path;
pub mod remove;
pub mod set;
@ -10,7 +9,6 @@ pub mod set_into;
pub use clear::SubCommand as ConfigClear;
pub use command::Command as Config;
pub use get::SubCommand as ConfigGet;
pub use load::SubCommand as ConfigLoad;
pub use path::SubCommand as ConfigPath;
pub use remove::SubCommand as ConfigRemove;
pub use set::SubCommand as ConfigSet;

View File

@ -1,7 +1,7 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, UntaggedValue, Value};
pub struct SubCommand;
@ -33,9 +33,18 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn path(args: CommandArgs) -> Result<OutputStream, ShellError> {
let path = config::default_path()?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Primitive(Primitive::FilePath(path)).into_value(args.call_info.name_tag),
match args.scope.get_var("config-path") {
Some(
path
@
Value {
value: UntaggedValue::Primitive(Primitive::FilePath(_)),
..
},
) => path,
_ => UntaggedValue::Primitive(Primitive::FilePath(nu_data::config::default_path()?))
.into_value(args.call_info.name_tag),
},
)))
}

View File

@ -1,13 +1,13 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue};
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct RemoveArgs {
pub struct Arguments {
remove: Tagged<String>,
}
@ -44,15 +44,24 @@ impl WholeStreamCommand for SubCommand {
pub async fn remove(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let (RemoveArgs { remove }, _) = args.process().await?;
let scope = args.scope.clone();
let (Arguments { remove }, _) = args.process().await?;
let mut result = nu_data::config::read(name_span, &None)?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let mut result = nu_data::config::read(name_span, &path)?;
let key = remove.to_string();
if result.contains_key(&key) {
result.swap_remove(&key);
config::write(&result, &None)?;
config::write(&result, &path)?;
Ok(futures::stream::iter(vec![ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(remove.tag()),
)])

View File

@ -1,13 +1,15 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ColumnPath, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{
ColumnPath, Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value,
};
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SetArgs {
path: ColumnPath,
pub struct Arguments {
column_path: ColumnPath,
value: Value,
}
@ -58,13 +60,26 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_tag = args.call_info.name_tag.clone();
let (SetArgs { path, mut value }, _) = args.process().await?;
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (
Arguments {
column_path,
mut value,
},
_,
) = args.process().await?;
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let raw_entries = nu_data::config::read(&name_tag, &None)?;
let configuration = UntaggedValue::row(raw_entries).into_value(&name_tag);
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
let raw_entries = nu_data::config::read(&name, &path)?;
let configuration = UntaggedValue::row(raw_entries).into_value(&name);
if let UntaggedValue::Table(rows) = &value.value {
if rows.len() == 1 && rows[0].is_row() {
@ -72,15 +87,15 @@ pub async fn set(args: CommandArgs) -> Result<OutputStream, ShellError> {
}
}
match configuration.forgiving_insert_data_at_column_path(&path, value) {
match configuration.forgiving_insert_data_at_column_path(&column_path, value) {
Ok(Value {
value: UntaggedValue::Row(changes),
..
}) => {
config::write(&changes.entries, &None)?;
config::write(&changes.entries, &path)?;
Ok(OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(changes).into_value(name_tag),
UntaggedValue::Row(changes).into_value(name),
)))
}
Ok(_) => Ok(OutputStream::empty()),

View File

@ -1,13 +1,13 @@
use crate::prelude::*;
use nu_engine::WholeStreamCommand;
use nu_errors::ShellError;
use nu_protocol::{ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_protocol::{Primitive, ReturnSuccess, Signature, SyntaxShape, UntaggedValue, Value};
use nu_source::Tagged;
pub struct SubCommand;
#[derive(Deserialize)]
pub struct SetIntoArgs {
pub struct Arguments {
set_into: Tagged<String>,
}
@ -43,17 +43,19 @@ impl WholeStreamCommand for SubCommand {
}
pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
let name_span = args.call_info.name_tag.clone();
let name = args.call_info.name_tag.clone();
let scope = args.scope.clone();
let (Arguments { set_into: v }, input) = args.process().await?;
let (SetIntoArgs { set_into: v }, input) = args.process().await?;
let path = match scope.get_var("config-path") {
Some(Value {
value: UntaggedValue::Primitive(Primitive::FilePath(path)),
..
}) => Some(path),
_ => nu_data::config::default_path().ok(),
};
// NOTE: None because we are not loading a new config file, we just want to read from the
// existing config
let mut result = nu_data::config::read(name_span, &None)?;
// In the original code, this is set to `Some` if the `load flag is set`
let configuration = None;
let mut result = nu_data::config::read(&name, &path)?;
let rows: Vec<Value> = input.collect().await;
let key = v.to_string();
@ -70,7 +72,7 @@ pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
result.insert(key, value.clone());
config::write(&result, &configuration)?;
config::write(&result, &path)?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),
@ -81,7 +83,7 @@ pub async fn set_into(args: CommandArgs) -> Result<OutputStream, ShellError> {
result.insert(key, value);
config::write(&result, &configuration)?;
config::write(&result, &path)?;
OutputStream::one(ReturnSuccess::value(
UntaggedValue::Row(result.into()).into_value(name),

View File

@ -29,7 +29,6 @@ pub fn create_default_context(interactive: bool) -> Result<EvaluationContext, Bo
whole_stream_command(ConfigSet),
whole_stream_command(ConfigSetInto),
whole_stream_command(ConfigClear),
whole_stream_command(ConfigLoad),
whole_stream_command(ConfigRemove),
whole_stream_command(ConfigPath),
whole_stream_command(Help),

View File

@ -1,29 +1,21 @@
use nu_test_support::fs::Stub::FileWithContentToBeTrimmed;
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
use nu_test_support::pipeline as input;
use nu_test_support::playground::{says, Playground};
use hamcrest2::assert_that;
use hamcrest2::prelude::*;
#[test]
fn adds_a_row_to_the_end() {
Playground::setup("append_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
Playground::setup("append_test_1", |_, nu| {
assert_that!(
nu.pipeline(&input(
r#"
echo [ "Andrés N. Robalino", "Jonathan Turner", "Yehuda Katz" ]
| append "pollo loco"
| nth 3
"#
));
assert_eq!(actual.out, "pollo loco");
"#
)),
says().to_stdout("pollo loco")
);
})
}

View File

@ -1,5 +1,5 @@
use nu_test_support::fs::Stub::EmptyFile;
use nu_test_support::playground::{Dirs, Playground};
use nu_test_support::playground::Playground;
use nu_test_support::{nu, pipeline};
#[test]
@ -269,61 +269,57 @@ fn lists_files_including_starting_with_dot() {
#[test]
fn list_all_columns() {
Playground::setup(
"ls_test_all_columns",
|dirs: Dirs, sandbox: &mut Playground| {
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("")
}
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"
);
},
);
#[cfg(windows)]
{
[
"name", "type", "target", "readonly", "size", "created", "accessed", "modified",
]
.join("")
}
};
assert_eq!(
actual.out, expected,
"column names are incorrect for ls long"
);
});
}

View File

@ -1,7 +1,4 @@
mod append;
mod autoenv;
mod autoenv_trust;
mod autoenv_untrust;
mod cal;
mod cd;
mod compact;