Skip to content

Commit

Permalink
add multiline test script
Browse files Browse the repository at this point in the history
  • Loading branch information
wolfv committed Sep 7, 2024
1 parent 7f68c18 commit bd952bc
Show file tree
Hide file tree
Showing 11 changed files with 138 additions and 30 deletions.
49 changes: 49 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions crates/deno_task_shell/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pest_derive = "2.6.0"
dirs = "5.0.1"

[dev-dependencies]
insta = "1.40.0"
parking_lot = "0.12.1"
pretty_assertions = "1"
serde_json = "1.0.111"
Expand Down
19 changes: 9 additions & 10 deletions crates/deno_task_shell/src/grammar.pest
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// grammar.pest

// Whitespace and comments
WHITESPACE = _{ " " | "\t" | ("\\" ~ WHITESPACE* ~ NEWLINE) }
WHITESPACE = _{ " " | "\t" | ESCAPED_NEWLINE }
ESCAPED_NEWLINE = _{ "\\" ~ NEWLINE }
COMMENT = _{ "#" ~ (!NEWLINE ~ ANY)* }

// Basic tokens
Expand All @@ -25,11 +24,13 @@ QUOTED_PENDING_WORD = ${ (
QUOTED_ESCAPE_CHAR |
SUB_COMMAND |
("$" ~ VARIABLE) |
QUOTED_CHAR
QUOTED_CHAR |
NEWLINE |
ESCAPED_NEWLINE
)* }

UNQUOTED_ESCAPE_CHAR = ${ ("\\" ~ "$" | "$" ~ !"(" ~ !VARIABLE) | "\\" ~ (" " | "`" | "\"" | "(" | ")")* }
QUOTED_ESCAPE_CHAR = ${ "\\" ~ "$" | "$" ~ !"(" ~ !VARIABLE | "\\" ~ ("`" | "\"" | "(" | ")" | "'")* }
QUOTED_ESCAPE_CHAR = @{ "\\" ~ ("$" | "`" | "\"" | "(" | ")" | "'" | "\n" | "\r\n") }

UNQUOTED_CHAR = ${ ("\\" ~ " ") | !("(" | ")" | "{" | "}" | "<" | ">" | "|" | "&" | ";" | "\"" | "'" | "$") ~ ANY }
QUOTED_CHAR = ${ !"\"" ~ ANY }
Expand All @@ -38,12 +39,11 @@ VARIABLE = ${ (ASCII_ALPHANUMERIC | "_")+ }
SUB_COMMAND = { "$(" ~ complete_command ~ ")" }

DOUBLE_QUOTED = @{ "\"" ~ QUOTED_PENDING_WORD ~ "\"" }
SINGLE_QUOTED = @{ "'" ~ (!"'" ~ ANY)* ~ "'" }
SINGLE_QUOTED = @{ "'" ~ (!"'" ~ (NEWLINE | ESCAPED_NEWLINE | ANY))* ~ "'" }

NAME = ${ (ASCII_ALPHA | "_") ~ (ASCII_ALPHANUMERIC | "_")* }
ASSIGNMENT_WORD = { NAME ~ "=" ~ UNQUOTED_PENDING_WORD? }
IO_NUMBER = @{ ASCII_DIGIT+ }

// Special tokens
AND_IF = { "&&" }
OR_IF = { "||" }
Expand All @@ -60,7 +60,6 @@ CLOBBER = { ">|" }
AMPERSAND = { "&" }
EXIT_STATUS = ${ "$?" }


// Operators
OPERATOR = _{
AND_IF | OR_IF | DSEMI | DLESS | DGREAT | LESSAND | GREATAND | LESSGREAT | DLESSDASH | CLOBBER |
Expand Down Expand Up @@ -203,8 +202,8 @@ filename = _{ FILE_NAME_PENDING_WORD }
io_here = !{ (DLESS | DLESSDASH) ~ here_end }
here_end = @{ ("\"" ~ UNQUOTED_PENDING_WORD ~ "\"") | UNQUOTED_PENDING_WORD }

newline_list = _{ NEWLINE+ }
linebreak = _{ NEWLINE* }
newline_list = _{ (NEWLINE | ESCAPED_NEWLINE)+ }
linebreak = _{ (NEWLINE | ESCAPED_NEWLINE)* }
separator_op = { "&" | ";" }
separator = _{ separator_op ~ linebreak | newline_list }
sequential_sep = !{ ";" ~ linebreak | newline_list }
Expand Down
2 changes: 1 addition & 1 deletion crates/deno_task_shell/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
pub mod parser;

#[cfg(feature = "shell")]
mod shell;
pub mod shell;

#[cfg(feature = "shell")]
pub use shell::*;
10 changes: 6 additions & 4 deletions crates/deno_task_shell/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -677,7 +677,6 @@ fn parse_subshell(pair: Pair<Rule>) -> Result<Command> {

fn parse_word(pair: Pair<Rule>) -> Result<Word> {
let mut parts = Vec::new();

match pair.as_rule() {
Rule::UNQUOTED_PENDING_WORD => {
for part in pair.into_inner() {
Expand Down Expand Up @@ -803,10 +802,14 @@ fn parse_quoted_word(pair: Pair<Rule>) -> Result<WordPart> {
match part.as_rule() {
Rule::EXIT_STATUS => parts.push(WordPart::Text("$?".to_string())),
Rule::QUOTED_ESCAPE_CHAR => {
let val = &part.as_str()[1..];
if val == "\n" {
continue;
}
if let Some(WordPart::Text(ref mut s)) = parts.last_mut() {
s.push_str(part.as_str());
s.push_str(val);
} else {
parts.push(WordPart::Text(part.as_str().to_string()));
parts.push(WordPart::Text(val.to_string()));
}
}
Rule::SUB_COMMAND => {
Expand Down Expand Up @@ -990,7 +993,6 @@ mod test {
.map_err(|e| anyhow::Error::msg(e.to_string()))?
.next()
.unwrap();
// println!("pairs: {:?}", pairs);
parse_complete_command(pairs)
};

Expand Down
2 changes: 1 addition & 1 deletion crates/deno_task_shell/src/shell/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ mod types;
#[cfg(test)]
mod test;
#[cfg(test)]
mod test_builder;
pub mod test_builder;
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: crates/deno_task_shell/src/shell/test.rs
expression: stdout
---
foo bla
foo
bla
foo " bar
18 changes: 18 additions & 0 deletions crates/deno_task_shell/src/shell/test.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
// Copyright 2018-2024 the Deno authors. MIT license.

use std::path::PathBuf;

use futures::FutureExt;

use super::test_builder::TestBuilder;
use super::types::ExecuteResult;

const FOLDER_SEPARATOR: char = if cfg!(windows) { '\\' } else { '/' };

fn test_data() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../scripts")
}

#[tokio::test]
async fn commands() {
TestBuilder::new()
Expand Down Expand Up @@ -1481,3 +1487,15 @@ fn no_such_file_error_text() -> &'static str {
"No such file or directory (os error 2)"
}
}

#[tokio::test]
async fn multiline() {
let (stdout, _, _) = TestBuilder::new()
.command_from_file(&test_data().join("multiline.sh"))
.assert_exit_code(0)
.ignore_expected_stdout()
.run()
.await;

insta::assert_snapshot!(stdout);
}
50 changes: 37 additions & 13 deletions crates/deno_task_shell/src/shell/test_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use futures::future::LocalBoxFuture;
use pretty_assertions::assert_eq;
use std::collections::HashMap;
use std::fs;
use std::path::Path;
use std::path::PathBuf;
use std::rc::Rc;
use tokio::task::JoinHandle;
Expand Down Expand Up @@ -70,6 +71,7 @@ pub struct TestBuilder {
expected_exit_code: i32,
expected_stderr: String,
expected_stdout: String,
ignore_expected_stdout: bool,
assertions: Vec<TestAssertion>,
}

Expand Down Expand Up @@ -99,6 +101,7 @@ impl TestBuilder {
expected_stderr: Default::default(),
expected_stdout: Default::default(),
assertions: Default::default(),
ignore_expected_stdout: false,
}
}

Expand Down Expand Up @@ -139,6 +142,13 @@ impl TestBuilder {
self
}

// Run a file as a script
pub fn command_from_file(&mut self, path: &Path) -> &mut Self {
let script = fs::read_to_string(&path).unwrap();
self.command(&script);
self
}

pub fn custom_command(
&mut self,
name: &str,
Expand Down Expand Up @@ -200,7 +210,12 @@ impl TestBuilder {
self
}

pub async fn run(&mut self) {
pub fn ignore_expected_stdout(&mut self) -> &mut Self {
self.ignore_expected_stdout = true;
self
}

pub async fn run(&mut self) -> (String, String, i32) {
let list = parse(&self.command).unwrap();
let cwd = if let Some(temp_dir) = &self.temp_dir {
temp_dir.cwd.clone()
Expand All @@ -227,18 +242,25 @@ impl TestBuilder {
} else {
"NO_TEMP_DIR".to_string()
};
assert_eq!(
stderr_handle.await.unwrap(),
self.expected_stderr.replace("$TEMP_DIR", &temp_dir),
"\n\nFailed for: {}",
self.command
);
assert_eq!(
stdout_handle.await.unwrap(),
self.expected_stdout.replace("$TEMP_DIR", &temp_dir),
"\n\nFailed for: {}",
self.command
);

let stderr = stderr_handle.await.unwrap();

let stdout = stdout_handle.await.unwrap();
if !self.ignore_expected_stdout {
assert_eq!(
stderr,
self.expected_stderr.replace("$TEMP_DIR", &temp_dir),
"\n\nFailed for: {}",
self.command
);

assert_eq!(
stdout,
self.expected_stdout.replace("$TEMP_DIR", &temp_dir),
"\n\nFailed for: {}",
self.command
);
}
assert_eq!(
exit_code, self.expected_exit_code,
"\n\nFailed for: {}",
Expand Down Expand Up @@ -275,6 +297,8 @@ impl TestBuilder {
}
}
}

(stdout, stderr, exit_code)
}
}

Expand Down
2 changes: 1 addition & 1 deletion crates/shell/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ impl Completer for ShellCompleter {
}

fn extract_word(line: &str, pos: usize) -> (usize, &str) {
if line.ends_with(" ") {
if line.ends_with(' ') {
return (pos, "");
}
let words: Vec<_> = line[..pos].split_whitespace().collect();
Expand Down
7 changes: 7 additions & 0 deletions scripts/multiline.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
echo "foo \
bla"

echo "foo
bla"

echo "foo \" bar"

0 comments on commit bd952bc

Please sign in to comment.