Skip to content

Commit

Permalink
feat: support matching stdout for system (#206)
Browse files Browse the repository at this point in the history
  • Loading branch information
xxchan authored Apr 11, 2024
1 parent 58673cb commit 38030ac
Show file tree
Hide file tree
Showing 14 changed files with 215 additions and 43 deletions.
17 changes: 17 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

## [0.20.0] - 2024-04-08

* Show stdout, stderr when `system` command fails.
* Support matching stdout for `system`
```
system ok
echo "Hello, world!"
----
Hello, world!
```
Currently, only exact match is supported. Besides, the output cannot contain more than one blank lines in between. The record ends with two consecutive blank lines.

Some minor **Breaking changes**:
- Add field `stdout` to `parser::Record::System` and `runner::RecordOutput::System`, and mark them as `#[non_exhaustive]`.
- Change trait method `AsyncDB::run_command`'s return type from `std::process::ExitStatus` to `std::process::Output`.


## [0.19.1] - 2024-01-04

* parser: `include` now returns error if no file is matched.
Expand Down
6 changes: 3 additions & 3 deletions Cargo.lock

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

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ resolver = "2"
members = ["sqllogictest", "sqllogictest-bin", "sqllogictest-engines", "tests"]

[workspace.package]
version = "0.19.1"
version = "0.20.0"
edition = "2021"
homepage = "https://github.com/risinglightdb/sqllogictest-rs"
keywords = ["sql", "database", "parser", "cli"]
Expand Down
4 changes: 2 additions & 2 deletions sqllogictest-bin/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ glob = "0.3"
itertools = "0.11"
quick-junit = { version = "0.3" }
rand = "0.8"
sqllogictest = { path = "../sqllogictest", version = "0.19" }
sqllogictest-engines = { path = "../sqllogictest-engines", version = "0.19" }
sqllogictest = { path = "../sqllogictest", version = "0.20" }
sqllogictest-engines = { path = "../sqllogictest-engines", version = "0.20" }
tokio = { version = "1", features = [
"rt",
"rt-multi-thread",
Expand Down
5 changes: 2 additions & 3 deletions sqllogictest-bin/src/engines.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
use std::fmt::Display;
use std::process::ExitStatus;
use std::time::Duration;

use async_trait::async_trait;
Expand Down Expand Up @@ -130,7 +129,7 @@ impl AsyncDB for Engines {
tokio::time::sleep(dur).await
}

async fn run_command(command: std::process::Command) -> std::io::Result<ExitStatus> {
Command::from(command).status().await
async fn run_command(command: std::process::Command) -> std::io::Result<std::process::Output> {
Command::from(command).output().await
}
}
2 changes: 1 addition & 1 deletion sqllogictest-engines/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ postgres-types = { version = "0.2.5", features = ["derive", "with-chrono-0_4"] }
rust_decimal = { version = "1.30.0", features = ["tokio-pg"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
sqllogictest = { path = "../sqllogictest", version = "0.19" }
sqllogictest = { path = "../sqllogictest", version = "0.20" }
thiserror = "1"
tokio = { version = "1", features = [
"rt",
Expand Down
6 changes: 3 additions & 3 deletions sqllogictest-engines/src/external.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use std::io;
use std::marker::PhantomData;
use std::process::{ExitStatus, Stdio};
use std::process::Stdio;
use std::time::Duration;

use async_trait::async_trait;
Expand Down Expand Up @@ -121,8 +121,8 @@ impl AsyncDB for ExternalDriver {
tokio::time::sleep(dur).await
}

async fn run_command(command: std::process::Command) -> std::io::Result<ExitStatus> {
Command::from(command).status().await
async fn run_command(command: std::process::Command) -> std::io::Result<std::process::Output> {
Command::from(command).output().await
}
}

Expand Down
6 changes: 3 additions & 3 deletions sqllogictest-engines/src/postgres/extended.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt::Write;
use std::process::{Command, ExitStatus};
use std::process::Command;
use std::time::Duration;

use async_trait::async_trait;
Expand Down Expand Up @@ -319,7 +319,7 @@ impl sqllogictest::AsyncDB for Postgres<Extended> {
tokio::time::sleep(dur).await
}

async fn run_command(command: Command) -> std::io::Result<ExitStatus> {
tokio::process::Command::from(command).status().await
async fn run_command(command: Command) -> std::io::Result<std::process::Output> {
tokio::process::Command::from(command).output().await
}
}
6 changes: 3 additions & 3 deletions sqllogictest-engines/src/postgres/simple.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use std::process::{Command, ExitStatus};
use std::process::Command;
use std::time::Duration;

use async_trait::async_trait;
Expand Down Expand Up @@ -66,7 +66,7 @@ impl sqllogictest::AsyncDB for Postgres<Simple> {
tokio::time::sleep(dur).await
}

async fn run_command(command: Command) -> std::io::Result<ExitStatus> {
tokio::process::Command::from(command).status().await
async fn run_command(command: Command) -> std::io::Result<std::process::Output> {
tokio::process::Command::from(command).output().await
}
}
36 changes: 29 additions & 7 deletions sqllogictest/src/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -136,11 +136,13 @@ pub enum Record<T: ColumnType> {
},
/// A system command is an external command that is to be executed by the shell. Currently it
/// must succeed and the output is ignored.
#[non_exhaustive]
System {
loc: Location,
conditions: Vec<Condition>,
/// The external command.
command: String,
stdout: Option<String>,
},
/// A sleep period.
Sleep {
Expand Down Expand Up @@ -266,8 +268,13 @@ impl<T: ColumnType> std::fmt::Display for Record<T> {
loc: _,
conditions: _,
command,
stdout,
} => {
writeln!(f, "system ok\n{command}")
writeln!(f, "system ok\n{command}")?;
if let Some(stdout) = stdout {
writeln!(f, "----\n{}\n", stdout.trim())?;
}
Ok(())
}
Record::Sleep { loc: _, duration } => {
write!(f, "sleep {}", humantime::format_duration(*duration))
Expand Down Expand Up @@ -754,7 +761,7 @@ fn parse_inner<T: ColumnType>(loc: &Location, script: &str) -> Result<Vec<Record
[] => QueryExpect::empty_results(),
};

// The SQL for the query is found on second an subsequent lines of the record
// The SQL for the query is found on second and subsequent lines of the record
// up to first line of the form "----" or until the end of the record.
let (sql, has_result) = parse_lines(&mut lines, &loc, Some(RESULTS_DELIMITER))?;
if has_result {
Expand Down Expand Up @@ -789,11 +796,19 @@ fn parse_inner<T: ColumnType>(loc: &Location, script: &str) -> Result<Vec<Record
}
["system", "ok"] => {
// TODO: we don't support asserting error message for system command
let (command, _) = parse_lines(&mut lines, &loc, None)?;
// The command is found on second and subsequent lines of the record
// up to first line of the form "----" or until the end of the record.
let (command, has_result) = parse_lines(&mut lines, &loc, Some(RESULTS_DELIMITER))?;
let stdout = if has_result {
Some(parse_multiple_result(&mut lines))
} else {
None
};
records.push(Record::System {
loc,
conditions: std::mem::take(&mut conditions),
command,
stdout,
});
}
["control", res @ ..] => match res {
Expand Down Expand Up @@ -897,10 +912,10 @@ fn parse_lines<'a>(
Ok((out, found_delimiter))
}

/// Parse multiline error message under `----`.
fn parse_multiline_error<'a>(
/// Parse multiline output under `----`.
fn parse_multiple_result<'a>(
lines: &mut Peekable<impl Iterator<Item = (usize, &'a str)>>,
) -> ExpectedError {
) -> String {
let mut results = String::new();

while let Some((_, line)) = lines.next() {
Expand All @@ -913,7 +928,14 @@ fn parse_multiline_error<'a>(
results.push('\n');
}

ExpectedError::Multiline(results.trim().to_string())
results.trim().to_string()
}

/// Parse multiline error message under `----`.
fn parse_multiline_error<'a>(
lines: &mut Peekable<impl Iterator<Item = (usize, &'a str)>>,
) -> ExpectedError {
ExpectedError::Multiline(parse_multiple_result(lines))
}

#[cfg(test)]
Expand Down
Loading

0 comments on commit 38030ac

Please sign in to comment.