nushell/crates/nu-command/tests/format_conversions/tsv.rs

296 lines
8.0 KiB
Rust
Raw Normal View History

2019-12-17 19:54:39 +01:00
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 | 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 | 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(&[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
2020-05-24 08:41:30 +02:00
| split column "\t" a b c d origin
| last 1
| to tsv
| lines
2022-02-09 15:59:40 +01:00
| select 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(&[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
2020-05-24 08:41:30 +02:00
| 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(&[FileWithContentToBeTrimmed(
"los_tres_amigos.txt",
r#"
first Name Last Name rusty_luck
Andrés Robalino 1
JT 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]
Replace `ExternalStream` with new `ByteStream` type (#12774) # Description This PR introduces a `ByteStream` type which is a `Read`-able stream of bytes. Internally, it has an enum over three different byte stream sources: ```rust pub enum ByteStreamSource { Read(Box<dyn Read + Send + 'static>), File(File), Child(ChildProcess), } ``` This is in comparison to the current `RawStream` type, which is an `Iterator<Item = Vec<u8>>` and has to allocate for each read chunk. Currently, `PipelineData::ExternalStream` serves a weird dual role where it is either external command output or a wrapper around `RawStream`. `ByteStream` makes this distinction more clear (via `ByteStreamSource`) and replaces `PipelineData::ExternalStream` in this PR: ```rust pub enum PipelineData { Empty, Value(Value, Option<PipelineMetadata>), ListStream(ListStream, Option<PipelineMetadata>), ByteStream(ByteStream, Option<PipelineMetadata>), } ``` The PR is relatively large, but a decent amount of it is just repetitive changes. This PR fixes #7017, fixes #10763, and fixes #12369. This PR also improves performance when piping external commands. Nushell should, in most cases, have competitive pipeline throughput compared to, e.g., bash. | Command | Before (MB/s) | After (MB/s) | Bash (MB/s) | | -------------------------------------------------- | -------------:| ------------:| -----------:| | `throughput \| rg 'x'` | 3059 | 3744 | 3739 | | `throughput \| nu --testbin relay o> /dev/null` | 3508 | 8087 | 8136 | # User-Facing Changes - This is a breaking change for the plugin communication protocol, because the `ExternalStreamInfo` was replaced with `ByteStreamInfo`. Plugins now only have to deal with a single input stream, as opposed to the previous three streams: stdout, stderr, and exit code. - The output of `describe` has been changed for external/byte streams. - Temporary breaking change: `bytes starts-with` no longer works with byte streams. This is to keep the PR smaller, and `bytes ends-with` already does not work on byte streams. - If a process core dumped, then instead of having a `Value::Error` in the `exit_code` column of the output returned from `complete`, it now is a `Value::Int` with the negation of the signal number. # After Submitting - Update docs and book as necessary - Release notes (e.g., plugin protocol changes) - Adapt/convert commands to work with byte streams (high priority is `str length`, `bytes starts-with`, and maybe `bytes ends-with`). - Refactor the `tee` code, Devyn has already done some work on this. --------- Co-authored-by: Devyn Cairns <devyn.cairns@gmail.com>
2024-05-16 16:11:18 +02:00
#[ignore = "csv crate has a bug when the last line is a comment: https://github.com/BurntSushi/rust-csv/issues/363"]
fn from_tsv_text_with_comments_to_table() {
Playground::setup("filter_from_tsv_test_2", |dirs, sandbox| {
sandbox.with_files(&[FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"
# This is a comment
first_name last_name rusty_luck
# This one too
Andrés Robalino 1
Jonathan Turner 1
Yehuda Katz 1
# This one also
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r##"
open los_tres_caballeros.txt
| from tsv --comment "#"
| get rusty_luck
| length
"##
));
assert_eq!(actual.out, "3");
})
}
#[test]
fn from_tsv_text_with_custom_quotes_to_table() {
Playground::setup("filter_from_tsv_test_3", |dirs, sandbox| {
sandbox.with_files(&[FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"
first_name last_name rusty_luck
'And''rés' Robalino 1
Jonathan Turner 1
Yehuda Katz 1
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from tsv --quote "'"
| first
| get first_name
"#
));
assert_eq!(actual.out, "And'rés");
})
}
#[test]
fn from_tsv_text_with_custom_escapes_to_table() {
Playground::setup("filter_from_tsv_test_4", |dirs, sandbox| {
sandbox.with_files(&[FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"
first_name last_name rusty_luck
"And\"rés" Robalino 1
Jonathan Turner 1
Yehuda Katz 1
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r"
open los_tres_caballeros.txt
| from tsv --escape '\'
| first
| get first_name
"
));
assert_eq!(actual.out, "And\"rés");
})
}
#[test]
fn from_tsv_text_skipping_headers_to_table() {
Playground::setup("filter_from_tsv_test_5", |dirs, sandbox| {
sandbox.with_files(&[FileWithContentToBeTrimmed(
"los_tres_amigos.txt",
r#"
Andrés Robalino 1
JT Turner 1
Yehuda Katz 1
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_amigos.txt
| from tsv --noheaders
Make the subcommands (`from {csv, tsv, ssv}`) 0-based for consistency (#13209) # Description fixed #11678 The sub-commands of from command (`from {csv, tsv, ssv}`) name columns starting from index 0. This behaviour is inconsistent with other commands such as `detect columns`. This PR makes the subcommands index 0-based. # User-Facing Changes The subcommands (`from {csv, tsv, ssv}`) return a table with the columns starting at index 0 if no header data is passed. ``` ~/Development/nushell> "foo bar baz" | from ssv -n -m 1 ╭───┬─────────┬─────────┬─────────╮ │ # │ column0 │ column1 │ column2 │ ├───┼─────────┼─────────┼─────────┤ │ 0 │ foo │ bar │ baz │ ╰───┴─────────┴─────────┴─────────╯ ~/Development/nushell> "foo,bar,baz" | from csv -n ╭───┬─────────┬─────────┬─────────╮ │ # │ column0 │ column1 │ column2 │ ├───┼─────────┼─────────┼─────────┤ │ 0 │ foo │ bar │ baz │ ╰───┴─────────┴─────────┴─────────╯ ~/Development/nushell> "foo\tbar\tbaz" | from tsv -n ╭───┬─────────┬─────────┬─────────╮ │ # │ column0 │ column1 │ column2 │ ├───┼─────────┼─────────┼─────────┤ │ 0 │ foo │ bar │ baz │ ╰───┴─────────┴─────────┴─────────╯ ``` # Tests + Formatting When I ran tests, `commands::touch::change_file_mtime_to_reference` failed with the following error. The error also occurs in the master branch, so it's probably unrelated to these changes. (maybe a problem with my dev environment) ``` $ toolkit check pr ~~~~~~~~ failures: ---- commands::touch::change_file_mtime_to_reference stdout ---- === stderr thread 'commands::touch::change_file_mtime_to_reference' panicked at crates/nu-command/tests/commands/touch.rs:298:9: assertion `left == right` failed left: SystemTime { tv_sec: 1719149697, tv_nsec: 57576929 } right: SystemTime { tv_sec: 1719149697, tv_nsec: 78219489 } failures: commands::touch::change_file_mtime_to_reference test result: FAILED. 1533 passed; 1 failed; 32 ignored; 0 measured; 0 filtered out; finished in 10.87s error: test failed, to rerun pass `-p nu-command --test main` - :green_circle: `toolkit fmt` - :green_circle: `toolkit clippy` - :red_circle: `toolkit test` - :black_circle: `toolkit test stdlib` ``` # After Submitting nothing
2024-06-27 00:51:47 +02:00
| get column2
| length
"#
));
assert_eq!(actual.out, "3");
})
}
#[test]
fn from_tsv_text_with_missing_columns_to_table() {
Playground::setup("filter_from_tsv_test_6", |dirs, sandbox| {
sandbox.with_files(&[FileWithContentToBeTrimmed(
"los_tres_caballeros.txt",
r#"
first_name last_name rusty_luck
Andrés Robalino
Jonathan Turner 1
Yehuda Katz 1
"#,
)]);
let actual = nu!(
cwd: dirs.test(), pipeline(
r#"
open los_tres_caballeros.txt
| from tsv --flexible
| get -i rusty_luck
| compact
| length
"#
));
assert_eq!(actual.out, "2");
})
}
#[test]
fn from_tsv_text_with_multiple_char_comment() {
Playground::setup("filter_from_tsv_test_7", |dirs, sandbox| {
sandbox.with_files(&[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 --comment "li"
"#
));
assert!(actual.err.contains("single character separator"));
})
}
#[test]
fn from_tsv_text_with_wrong_type_comment() {
Playground::setup("filter_from_csv_test_8", |dirs, sandbox| {
sandbox.with_files(&[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 --comment ('123' | into int)
"#
));
assert!(actual.err.contains("can't convert int to char"));
})
}