first pass of merging in warp (doesn't build)
This commit is contained in:
@@ -0,0 +1,223 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use galaxy_util::path::EscapeChar;
|
||||
|
||||
use super::super::lexer::Lexer;
|
||||
use super::super::{Command, Part};
|
||||
use super::*;
|
||||
use crate::parsers::simple::{
|
||||
command_without_leading_env_vars, decompose_command, top_level_command,
|
||||
};
|
||||
|
||||
#[test]
|
||||
fn test_parse_open_subshell() {
|
||||
let source = r#"cat "Hello $(ls -la"#;
|
||||
let command = Parser::new(Lexer::new(source, EscapeChar::Backslash, false)).parse_command();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
Command::new(vec![
|
||||
Part::Literal("cat".into()).spanned((0, 3)),
|
||||
Part::Concatenated(vec![
|
||||
Part::Literal("Hello ".into()).spanned((4, 11)),
|
||||
Part::OpenSubshell(vec![Command::new(vec![
|
||||
Part::Literal("ls".into()).spanned((13, 15)),
|
||||
Part::Literal("-la".into()).spanned((16, 19)),
|
||||
])
|
||||
.spanned((13, 19)),])
|
||||
.spanned((11, 19)),
|
||||
])
|
||||
.spanned((4, 19)),
|
||||
])
|
||||
.spanned((0, 19)),
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_nested_command() {
|
||||
let source = r#"cat "Hello $(ls -la)""#;
|
||||
let command = Parser::new(Lexer::new(source, EscapeChar::Backslash, false)).parse_command();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
Command::new(vec![
|
||||
Part::Literal("cat".into()).spanned((0, 3)),
|
||||
Part::Concatenated(vec![
|
||||
Part::Literal("Hello ".into()).spanned((4, 11)),
|
||||
Part::ClosedSubshell(vec![Command::new(vec![
|
||||
Part::Literal("ls".into()).spanned((13, 15)),
|
||||
Part::Literal("-la".into()).spanned((16, 19)),
|
||||
])
|
||||
.spanned((13, 19)),])
|
||||
.spanned((11, 20)),
|
||||
])
|
||||
.spanned((4, 21)),
|
||||
])
|
||||
.spanned((0, 21))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse() {
|
||||
let source = r#"ls | rm -rf || touch 'hello.txt\' &
|
||||
cat "Hello $(ls -la)" && echo `ps \`; {echo Goodbye😀}"#;
|
||||
|
||||
let commands = Parser::new(Lexer::new(source, EscapeChar::Backslash, false))
|
||||
.parse()
|
||||
.commands;
|
||||
|
||||
assert_eq!(
|
||||
commands,
|
||||
[
|
||||
Command::new(vec![Part::Literal("ls".into()).spanned((0, 2))]).spanned((0, 3)),
|
||||
Command::new(vec![
|
||||
Part::Literal("rm".into()).spanned((5, 7)),
|
||||
Part::Literal("-rf".into()).spanned((8, 11))
|
||||
])
|
||||
.spanned((5, 12)),
|
||||
Command::new(vec![
|
||||
Part::Literal("touch".into()).spanned((15, 20)),
|
||||
Part::Literal("hello.txt\\".into()).spanned((21, 33))
|
||||
])
|
||||
.spanned((15, 34)),
|
||||
Command::new(vec![
|
||||
Part::Literal("cat".into()).spanned((36, 39)),
|
||||
Part::Concatenated(vec![
|
||||
Part::Literal("Hello ".into()).spanned((40, 47)),
|
||||
Part::ClosedSubshell(vec![Command::new(vec![
|
||||
Part::Literal("ls".into()).spanned((49, 51)),
|
||||
Part::Literal("-la".into()).spanned((52, 55)),
|
||||
])
|
||||
.spanned((49, 55))])
|
||||
.spanned((47, 56)),
|
||||
])
|
||||
.spanned((40, 57))
|
||||
])
|
||||
.spanned((36, 58)),
|
||||
Command::new(vec![
|
||||
Part::Literal("echo".into()).spanned((61, 65)),
|
||||
Part::ClosedSubshell(vec![Command::new(vec![
|
||||
Part::Literal("ps".into()).spanned((67, 69)),
|
||||
Part::Literal("\\".into()).spanned((70, 71)),
|
||||
])
|
||||
.spanned((67, 71))])
|
||||
.spanned((66, 72)),
|
||||
])
|
||||
.spanned((61, 72)),
|
||||
Command::new(vec![
|
||||
Part::Literal("echo".into()).spanned((75, 79)),
|
||||
Part::Literal("Goodbye😀".into()).spanned((80, 91))
|
||||
])
|
||||
.spanned((75, 91)),
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
// Test that a backslash is retained when preceding a command.
|
||||
#[test]
|
||||
fn test_backslash_before_command() {
|
||||
let source = r#"\ls"#;
|
||||
let command = Parser::new(Lexer::new(source, EscapeChar::Backslash, false)).parse_command();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
Command::new(vec![Part::Literal(r"\ls".into()).spanned((0, 3))]).spanned((0, 3))
|
||||
);
|
||||
}
|
||||
|
||||
// Test that a backslash is not retained in the middle of a command.
|
||||
#[test]
|
||||
fn test_backslash_in_command() {
|
||||
let source = r#"ls \-la"#;
|
||||
let command = Parser::new(Lexer::new(source, EscapeChar::Backslash, false)).parse_command();
|
||||
|
||||
assert_eq!(
|
||||
command,
|
||||
Command::new(vec![
|
||||
Part::Literal("ls".into()).spanned((0, 2)),
|
||||
Part::Literal("-la".into()).spanned((3, 7))
|
||||
])
|
||||
.spanned((0, 7))
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_decompose_command() {
|
||||
let test_data = vec![
|
||||
("ls", vec!["ls"]),
|
||||
("$(ls)", vec!["ls"]),
|
||||
("ls -la", vec!["ls -la"]),
|
||||
("ls && cat", vec!["ls", "cat"]),
|
||||
(
|
||||
"ls $(foo | echo)",
|
||||
vec!["foo", "echo", "foo | echo", "ls $(foo | echo)"],
|
||||
),
|
||||
];
|
||||
|
||||
for (input, expected_output) in test_data {
|
||||
// Compare with hashsets bc we don't care about ordering.
|
||||
assert_eq!(
|
||||
HashSet::<String>::from_iter(decompose_command(input, EscapeChar::Backslash).0),
|
||||
HashSet::from_iter(expected_output.into_iter().map(ToString::to_string)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_command_without_leading_env_vars() {
|
||||
let test_data = vec![
|
||||
("X=1 rm -rf target", Some("rm -rf target")),
|
||||
(
|
||||
"X=1 Y=2 curl https://example.com",
|
||||
Some("curl https://example.com"),
|
||||
),
|
||||
("rm -rf target", Some("rm -rf target")),
|
||||
("X=1", None),
|
||||
];
|
||||
|
||||
for (input, expected_output) in test_data {
|
||||
assert_eq!(
|
||||
command_without_leading_env_vars(input, EscapeChar::Backslash),
|
||||
expected_output.map(ToString::to_string)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_contains_redirection() {
|
||||
let test_data = vec![
|
||||
("ls < \"file.txt < tmp\"", true),
|
||||
("echo $(ls > file.txt)", true),
|
||||
("ls >> file.txt", true),
|
||||
("ls < file.txt", true),
|
||||
("foo arg1 arg2 > file.txt", true),
|
||||
("foo && ls > file.txt", true),
|
||||
("echo \"hello world\" > output.txt", true),
|
||||
("echo \"5>4\"", false),
|
||||
("echo \"This message -> shows direction\"", false),
|
||||
("print(\"Value must be > 0 and < 100\")", false),
|
||||
];
|
||||
|
||||
for (cmd, should_contain_redirection) in test_data {
|
||||
let parser = Parser::new(Lexer::new(cmd, EscapeChar::Backslash, false));
|
||||
let contains_redirection = parser.parse().contains_redirection;
|
||||
assert_eq!(contains_redirection, should_contain_redirection);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_top_level_command() {
|
||||
let test_data = vec![
|
||||
("PAGER=0 git log", Some("git")),
|
||||
("PAGER= git log", Some("git")),
|
||||
("ls && git status", Some("ls")),
|
||||
("$(git status)", None),
|
||||
];
|
||||
|
||||
for (input, expected_output) in test_data {
|
||||
assert_eq!(
|
||||
top_level_command(input, EscapeChar::Backslash),
|
||||
expected_output.map(ToString::to_string)
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user