2020-05-18 12:56:01 +00:00
|
|
|
use nu_errors::ShellError;
|
|
|
|
use nu_protocol::hir::ClassifiedBlock;
|
2020-10-03 14:06:02 +00:00
|
|
|
use nu_protocol::{
|
2020-10-06 10:21:20 +00:00
|
|
|
Primitive, ReturnSuccess, Scope, ShellTypeName, Signature, SyntaxShape, UntaggedValue, Value,
|
2020-10-03 14:06:02 +00:00
|
|
|
};
|
|
|
|
use nu_source::{AnchorLocation, TaggedItem};
|
|
|
|
|
|
|
|
use crate::prelude::*;
|
|
|
|
|
|
|
|
use num_bigint::BigInt;
|
|
|
|
|
|
|
|
use crate::command_registry::CommandRegistry;
|
2020-05-18 12:56:01 +00:00
|
|
|
use crate::commands::classified::block::run_block;
|
2020-10-03 14:06:02 +00:00
|
|
|
use crate::commands::command::CommandArgs;
|
|
|
|
use crate::commands::{
|
2020-10-14 09:36:11 +00:00
|
|
|
whole_stream_command, BuildString, Command, Each, Echo, First, Get, Keep, Last, Nth,
|
|
|
|
StrCollect, WholeStreamCommand, Wrap,
|
2020-10-03 14:06:02 +00:00
|
|
|
};
|
2020-09-19 21:29:51 +00:00
|
|
|
use crate::evaluation_context::EvaluationContext;
|
2020-10-03 14:06:02 +00:00
|
|
|
use crate::stream::{InputStream, OutputStream};
|
|
|
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
use futures::executor::block_on;
|
|
|
|
use serde::Deserialize;
|
|
|
|
|
|
|
|
pub fn test_examples(cmd: Command) -> Result<(), ShellError> {
|
|
|
|
let examples = cmd.examples();
|
|
|
|
|
|
|
|
let mut base_context = EvaluationContext::basic()?;
|
|
|
|
|
|
|
|
base_context.add_commands(vec![
|
|
|
|
// Mocks
|
|
|
|
whole_stream_command(MockLs {}),
|
|
|
|
// Minimal restricted commands to aid in testing
|
|
|
|
whole_stream_command(Echo {}),
|
|
|
|
whole_stream_command(BuildString {}),
|
2020-10-14 09:36:11 +00:00
|
|
|
whole_stream_command(First {}),
|
2020-10-03 14:06:02 +00:00
|
|
|
whole_stream_command(Get {}),
|
|
|
|
whole_stream_command(Keep {}),
|
|
|
|
whole_stream_command(Each {}),
|
2020-10-14 09:36:11 +00:00
|
|
|
whole_stream_command(Last {}),
|
|
|
|
whole_stream_command(Nth {}),
|
2020-10-03 14:06:02 +00:00
|
|
|
whole_stream_command(StrCollect),
|
2020-10-06 10:21:20 +00:00
|
|
|
whole_stream_command(Wrap),
|
2020-10-03 14:06:02 +00:00
|
|
|
cmd,
|
|
|
|
]);
|
2020-05-18 12:56:01 +00:00
|
|
|
|
2020-10-03 14:06:02 +00:00
|
|
|
for sample_pipeline in examples {
|
|
|
|
let mut ctx = base_context.clone();
|
|
|
|
|
|
|
|
let block = parse_line(sample_pipeline.example, &mut ctx)?;
|
|
|
|
|
|
|
|
if let Some(expected) = &sample_pipeline.result {
|
|
|
|
let result = block_on(evaluate_block(block, &mut ctx))?;
|
|
|
|
|
2020-10-08 21:47:51 +00:00
|
|
|
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
|
|
|
|
.map_or(Ok(()), Err)?;
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
if expected.len() != result.len() {
|
|
|
|
let rows_returned =
|
|
|
|
format!("expected: {}\nactual: {}", expected.len(), result.len());
|
|
|
|
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
|
|
|
|
|
|
|
panic!(
|
|
|
|
"example command produced unexpected number of results.\n {} {}",
|
|
|
|
failed_call, rows_returned
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (e, a) in expected.iter().zip(result.iter()) {
|
|
|
|
if !values_equal(e, a) {
|
|
|
|
let row_errored = format!("expected: {:#?}\nactual: {:#?}", e, a);
|
|
|
|
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
|
|
|
|
|
|
|
panic!(
|
|
|
|
"example command produced unexpected result.\n {} {}",
|
|
|
|
failed_call, row_errored
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test(cmd: impl WholeStreamCommand + 'static) -> Result<(), ShellError> {
|
2020-05-18 12:56:01 +00:00
|
|
|
let examples = cmd.examples();
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
let mut base_context = EvaluationContext::basic()?;
|
2020-07-03 08:43:55 +00:00
|
|
|
|
2020-05-18 12:56:01 +00:00
|
|
|
base_context.add_commands(vec![
|
|
|
|
whole_stream_command(Echo {}),
|
2020-07-03 08:43:55 +00:00
|
|
|
whole_stream_command(BuildString {}),
|
2020-10-03 14:06:02 +00:00
|
|
|
whole_stream_command(Get {}),
|
|
|
|
whole_stream_command(Keep {}),
|
2020-08-27 05:44:18 +00:00
|
|
|
whole_stream_command(Each {}),
|
2020-05-18 12:56:01 +00:00
|
|
|
whole_stream_command(cmd),
|
2020-07-20 23:33:39 +00:00
|
|
|
whole_stream_command(StrCollect),
|
2020-10-06 10:21:20 +00:00
|
|
|
whole_stream_command(Wrap),
|
2020-05-18 12:56:01 +00:00
|
|
|
]);
|
|
|
|
|
2020-10-03 14:06:02 +00:00
|
|
|
for sample_pipeline in examples {
|
2020-05-18 12:56:01 +00:00
|
|
|
let mut ctx = base_context.clone();
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
let block = parse_line(sample_pipeline.example, &mut ctx)?;
|
|
|
|
|
|
|
|
if let Some(expected) = &sample_pipeline.result {
|
|
|
|
let result = block_on(evaluate_block(block, &mut ctx))?;
|
|
|
|
|
2020-10-08 21:47:51 +00:00
|
|
|
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
|
|
|
|
.map_or(Ok(()), Err)?;
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
if expected.len() != result.len() {
|
|
|
|
let rows_returned =
|
|
|
|
format!("expected: {}\nactual: {}", expected.len(), result.len());
|
|
|
|
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
|
|
|
|
|
|
|
panic!(
|
|
|
|
"example command produced unexpected number of results.\n {} {}",
|
|
|
|
failed_call, rows_returned
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (e, a) in expected.iter().zip(result.iter()) {
|
|
|
|
if !values_equal(e, a) {
|
|
|
|
let row_errored = format!("expected: {:#?}\nactual: {:#?}", e, a);
|
|
|
|
let failed_call = format!("command: {}\n", sample_pipeline.example);
|
|
|
|
|
|
|
|
panic!(
|
|
|
|
"example command produced unexpected result.\n {} {}",
|
|
|
|
failed_call, row_errored
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2020-05-18 12:56:01 +00:00
|
|
|
}
|
|
|
|
}
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
Ok(())
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn test_anchors(cmd: Command) -> Result<(), ShellError> {
|
|
|
|
let examples = cmd.examples();
|
|
|
|
|
|
|
|
let mut base_context = EvaluationContext::basic()?;
|
|
|
|
|
|
|
|
base_context.add_commands(vec![
|
|
|
|
// Minimal restricted commands to aid in testing
|
|
|
|
whole_stream_command(MockCommand {}),
|
|
|
|
whole_stream_command(MockEcho {}),
|
|
|
|
whole_stream_command(MockLs {}),
|
|
|
|
whole_stream_command(BuildString {}),
|
2020-10-14 09:36:11 +00:00
|
|
|
whole_stream_command(First {}),
|
2020-10-03 14:06:02 +00:00
|
|
|
whole_stream_command(Get {}),
|
|
|
|
whole_stream_command(Keep {}),
|
|
|
|
whole_stream_command(Each {}),
|
2020-10-14 09:36:11 +00:00
|
|
|
whole_stream_command(Last {}),
|
|
|
|
whole_stream_command(Nth {}),
|
2020-10-03 14:06:02 +00:00
|
|
|
whole_stream_command(StrCollect),
|
2020-10-06 10:21:20 +00:00
|
|
|
whole_stream_command(Wrap),
|
2020-10-03 14:06:02 +00:00
|
|
|
cmd,
|
|
|
|
]);
|
|
|
|
|
|
|
|
for sample_pipeline in examples {
|
|
|
|
let pipeline_with_anchor = format!("mock --open --path | {}", sample_pipeline.example);
|
|
|
|
|
|
|
|
let mut ctx = base_context.clone();
|
|
|
|
|
|
|
|
let block = parse_line(&pipeline_with_anchor, &mut ctx)?;
|
|
|
|
let result = block_on(evaluate_block(block, &mut ctx))?;
|
|
|
|
|
2020-10-08 21:47:51 +00:00
|
|
|
ctx.with_errors(|reasons| reasons.iter().cloned().take(1).next())
|
|
|
|
.map_or(Ok(()), Err)?;
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
for actual in result.iter() {
|
|
|
|
if !is_anchor_carried(actual, mock_path()) {
|
|
|
|
let failed_call = format!("command: {}\n", pipeline_with_anchor);
|
|
|
|
|
|
|
|
panic!(
|
|
|
|
"example command didn't carry anchor tag correctly.\n {} {:#?} {:#?}",
|
|
|
|
failed_call,
|
|
|
|
actual,
|
|
|
|
mock_path()
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(())
|
2020-05-18 12:56:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Parse and run a nushell pipeline
|
2020-10-03 14:06:02 +00:00
|
|
|
fn parse_line(line: &str, ctx: &mut EvaluationContext) -> Result<ClassifiedBlock, ShellError> {
|
2020-11-22 00:37:16 +00:00
|
|
|
let line = if let Some(line) = line.strip_suffix('\n') {
|
|
|
|
line
|
2020-05-18 12:56:01 +00:00
|
|
|
} else {
|
|
|
|
line
|
|
|
|
};
|
|
|
|
|
2020-11-09 16:27:07 +00:00
|
|
|
let (lite_result, err) = nu_parser::lite_parse(&line, 0);
|
|
|
|
if let Some(err) = err {
|
|
|
|
return Err(err.into());
|
|
|
|
}
|
2020-05-18 12:56:01 +00:00
|
|
|
|
|
|
|
// TODO ensure the command whose examples we're testing is actually in the pipeline
|
2020-10-26 06:55:52 +00:00
|
|
|
let classified_block = nu_parser::classify_block(&lite_result, ctx.registry());
|
2020-05-18 12:56:01 +00:00
|
|
|
Ok(classified_block)
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn evaluate_block(
|
|
|
|
block: ClassifiedBlock,
|
2020-09-19 21:29:51 +00:00
|
|
|
ctx: &mut EvaluationContext,
|
2020-05-18 12:56:01 +00:00
|
|
|
) -> Result<Vec<Value>, ShellError> {
|
|
|
|
let input_stream = InputStream::empty();
|
|
|
|
let env = ctx.get_env();
|
|
|
|
|
2020-09-25 23:40:02 +00:00
|
|
|
let scope = Scope::from_env(env);
|
|
|
|
|
|
|
|
Ok(run_block(&block.block, ctx, input_stream, scope)
|
|
|
|
.await?
|
2020-10-03 14:06:02 +00:00
|
|
|
.drain_vec()
|
2020-09-25 23:40:02 +00:00
|
|
|
.await)
|
2020-05-18 12:56:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// TODO probably something already available to do this
|
|
|
|
// TODO perhaps better panic messages when things don't compare
|
|
|
|
|
|
|
|
// Deep value comparisons that ignore tags
|
|
|
|
fn values_equal(expected: &Value, actual: &Value) -> bool {
|
|
|
|
use nu_protocol::UntaggedValue::*;
|
|
|
|
|
|
|
|
match (&expected.value, &actual.value) {
|
|
|
|
(Primitive(e), Primitive(a)) => e == a,
|
|
|
|
(Row(e), Row(a)) => {
|
|
|
|
if e.entries.len() != a.entries.len() {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
e.entries
|
|
|
|
.iter()
|
|
|
|
.zip(a.entries.iter())
|
|
|
|
.all(|((ek, ev), (ak, av))| ek == ak && values_equal(ev, av))
|
|
|
|
}
|
|
|
|
(Table(e), Table(a)) => e.iter().zip(a.iter()).all(|(e, a)| values_equal(e, a)),
|
|
|
|
(e, a) => unimplemented!("{} {}", e.type_name(), a.type_name()),
|
|
|
|
}
|
|
|
|
}
|
2020-10-03 14:06:02 +00:00
|
|
|
|
|
|
|
fn is_anchor_carried(actual: &Value, anchor: AnchorLocation) -> bool {
|
|
|
|
actual.tag.anchor() == Some(anchor)
|
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
|
|
|
struct Arguments {
|
|
|
|
path: Option<bool>,
|
|
|
|
open: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MockCommand;
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl WholeStreamCommand for MockCommand {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
"mock"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
|
|
|
Signature::build("mock")
|
|
|
|
.switch("open", "fake opening sources", Some('o'))
|
|
|
|
.switch("path", "file open", Some('p'))
|
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
|
|
|
"Generates tables and metadata that mimics behavior of real commands in controlled ways."
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn run(
|
|
|
|
&self,
|
|
|
|
args: CommandArgs,
|
|
|
|
registry: &CommandRegistry,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
|
|
|
let name_tag = args.call_info.name_tag.clone();
|
|
|
|
|
|
|
|
let (
|
|
|
|
Arguments {
|
|
|
|
path: mocked_path,
|
|
|
|
open: open_mock,
|
|
|
|
},
|
|
|
|
_input,
|
|
|
|
) = args.process(®istry).await?;
|
|
|
|
|
|
|
|
let out = UntaggedValue::string("Yehuda Katz in Ecuador");
|
|
|
|
|
|
|
|
if open_mock {
|
|
|
|
if let Some(true) = mocked_path {
|
2020-10-06 10:21:20 +00:00
|
|
|
return Ok(OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
|
|
|
value: out,
|
|
|
|
tag: Tag {
|
|
|
|
anchor: Some(mock_path()),
|
|
|
|
span: name_tag.span,
|
|
|
|
},
|
|
|
|
}))));
|
2020-10-03 14:06:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(OutputStream::one(Ok(ReturnSuccess::Value(
|
|
|
|
out.into_value(name_tag),
|
|
|
|
))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MockEcho;
|
|
|
|
|
|
|
|
#[derive(Deserialize)]
|
2020-10-06 10:21:20 +00:00
|
|
|
struct MockEchoArgs {
|
2020-10-03 14:06:02 +00:00
|
|
|
pub rest: Vec<Value>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl WholeStreamCommand for MockEcho {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
"echo"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
|
|
|
Signature::build("echo").rest(SyntaxShape::Any, "the values to echo")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
|
|
|
"Mock echo."
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn run(
|
|
|
|
&self,
|
|
|
|
args: CommandArgs,
|
|
|
|
registry: &CommandRegistry,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
|
|
|
let name_tag = args.call_info.name_tag.clone();
|
|
|
|
let (MockEchoArgs { rest }, input) = args.process(®istry).await?;
|
|
|
|
|
|
|
|
let mut base_value = UntaggedValue::string("Yehuda Katz in Ecuador").into_value(name_tag);
|
|
|
|
let input: Vec<Value> = input.collect().await;
|
|
|
|
|
|
|
|
if let Some(first) = input.get(0) {
|
|
|
|
base_value = first.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
let stream = rest.into_iter().map(move |i| {
|
|
|
|
let base_value = base_value.clone();
|
|
|
|
match i.as_string() {
|
2020-10-06 10:21:20 +00:00
|
|
|
Ok(s) => OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
|
|
|
value: UntaggedValue::Primitive(Primitive::String(s)),
|
2020-10-08 21:47:51 +00:00
|
|
|
tag: base_value.tag,
|
2020-10-06 10:21:20 +00:00
|
|
|
}))),
|
2020-10-03 14:06:02 +00:00
|
|
|
_ => match i {
|
|
|
|
Value {
|
|
|
|
value: UntaggedValue::Table(table),
|
|
|
|
..
|
2020-10-14 09:36:11 +00:00
|
|
|
} => {
|
|
|
|
if table.len() == 1 && table[0].is_table() {
|
|
|
|
let mut values: Vec<Value> =
|
|
|
|
table[0].table_entries().map(Clone::clone).collect();
|
|
|
|
|
|
|
|
for v in values.iter_mut() {
|
2020-10-03 14:06:02 +00:00
|
|
|
v.tag = base_value.tag();
|
2020-10-14 09:36:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
let subtable =
|
|
|
|
vec![UntaggedValue::Table(values).into_value(base_value.tag())];
|
|
|
|
|
|
|
|
futures::stream::iter(subtable.into_iter().map(ReturnSuccess::value))
|
|
|
|
.to_output_stream()
|
|
|
|
} else {
|
|
|
|
futures::stream::iter(
|
|
|
|
table
|
|
|
|
.into_iter()
|
|
|
|
.map(move |mut v| {
|
|
|
|
v.tag = base_value.tag();
|
|
|
|
v
|
|
|
|
})
|
|
|
|
.map(ReturnSuccess::value),
|
|
|
|
)
|
|
|
|
.to_output_stream()
|
|
|
|
}
|
|
|
|
}
|
2020-10-03 14:06:02 +00:00
|
|
|
_ => OutputStream::one(Ok(ReturnSuccess::Value(Value {
|
|
|
|
value: i.value.clone(),
|
2020-10-08 21:47:51 +00:00
|
|
|
tag: base_value.tag,
|
2020-10-03 14:06:02 +00:00
|
|
|
}))),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
Ok(futures::stream::iter(stream).flatten().to_output_stream())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
struct MockLs;
|
|
|
|
|
|
|
|
#[async_trait]
|
|
|
|
impl WholeStreamCommand for MockLs {
|
|
|
|
fn name(&self) -> &str {
|
|
|
|
"ls"
|
|
|
|
}
|
|
|
|
|
|
|
|
fn signature(&self) -> Signature {
|
|
|
|
Signature::build("ls")
|
|
|
|
}
|
|
|
|
|
|
|
|
fn usage(&self) -> &str {
|
|
|
|
"Mock ls."
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn run(
|
|
|
|
&self,
|
|
|
|
args: CommandArgs,
|
|
|
|
_: &CommandRegistry,
|
|
|
|
) -> Result<OutputStream, ShellError> {
|
|
|
|
let name_tag = args.call_info.name_tag.clone();
|
|
|
|
|
|
|
|
let mut base_value =
|
|
|
|
UntaggedValue::string("Andrés N. Robalino in Portland").into_value(name_tag);
|
|
|
|
let input: Vec<Value> = args.input.collect().await;
|
|
|
|
|
|
|
|
if let Some(first) = input.get(0) {
|
|
|
|
base_value = first.clone()
|
|
|
|
}
|
|
|
|
|
|
|
|
Ok(futures::stream::iter(
|
|
|
|
file_listing()
|
|
|
|
.iter()
|
|
|
|
.map(|row| Value {
|
|
|
|
value: row.value.clone(),
|
|
|
|
tag: base_value.tag.clone(),
|
|
|
|
})
|
|
|
|
.collect::<Vec<_>>()
|
|
|
|
.into_iter()
|
|
|
|
.map(ReturnSuccess::value),
|
|
|
|
)
|
|
|
|
.to_output_stream())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn int(s: impl Into<BigInt>) -> Value {
|
|
|
|
UntaggedValue::int(s).into_untagged_value()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn string(input: impl Into<String>) -> Value {
|
|
|
|
UntaggedValue::string(input.into()).into_untagged_value()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn date(input: impl Into<String>) -> Value {
|
|
|
|
let key = input.into().tagged_unknown();
|
|
|
|
crate::value::Date::naive_from_str(key.borrow_tagged())
|
|
|
|
.expect("date from string failed")
|
|
|
|
.into_untagged_value()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn file_listing() -> Vec<Value> {
|
|
|
|
vec![
|
2020-10-20 09:07:13 +00:00
|
|
|
row! {
|
2020-10-03 14:06:02 +00:00
|
|
|
"name".to_string() => string("Andrés.txt"),
|
|
|
|
"type".to_string() => string("File"),
|
|
|
|
"chickens".to_string() => int(10),
|
2020-10-20 09:07:13 +00:00
|
|
|
"modified".to_string() => date("2019-07-23")
|
|
|
|
},
|
|
|
|
row! {
|
2020-10-03 14:06:02 +00:00
|
|
|
"name".to_string() => string("Jonathan"),
|
|
|
|
"type".to_string() => string("Dir"),
|
|
|
|
"chickens".to_string() => int(5),
|
2020-10-20 09:07:13 +00:00
|
|
|
"modified".to_string() => date("2019-07-23")
|
|
|
|
},
|
|
|
|
row! {
|
2020-10-03 14:06:02 +00:00
|
|
|
"name".to_string() => string("Andrés.txt"),
|
|
|
|
"type".to_string() => string("File"),
|
|
|
|
"chickens".to_string() => int(20),
|
2020-10-20 09:07:13 +00:00
|
|
|
"modified".to_string() => date("2019-09-24")
|
|
|
|
},
|
|
|
|
row! {
|
2020-10-03 14:06:02 +00:00
|
|
|
"name".to_string() => string("Yehuda"),
|
|
|
|
"type".to_string() => string("Dir"),
|
|
|
|
"chickens".to_string() => int(4),
|
2020-10-20 09:07:13 +00:00
|
|
|
"modified".to_string() => date("2019-09-24")
|
|
|
|
},
|
2020-10-03 14:06:02 +00:00
|
|
|
]
|
|
|
|
}
|
|
|
|
|
|
|
|
fn mock_path() -> AnchorLocation {
|
|
|
|
let path = String::from("path/to/las_best_arepas_in_the_world.txt");
|
|
|
|
|
|
|
|
AnchorLocation::File(path)
|
|
|
|
}
|