Skip to content

Commit d42eae2

Browse files
authored
feat: add junit support (#54)
* feat: add junit support Signed-off-by: Alex Chi <iskyzh@gmail.com> * bump to 0.5.3 Signed-off-by: Alex Chi <iskyzh@gmail.com>
1 parent b197296 commit d42eae2

File tree

5 files changed

+76
-22
lines changed

5 files changed

+76
-22
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ jobs:
5555
with:
5656
command: test
5757
args: --no-fail-fast --all-features
58-
- name: Run all examples
58+
- name: Run examples
5959
run: |
6060
for example in examples/*.rs
6161
do

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
## [0.5.3] - 2022-06-26
10+
11+
### Added
12+
13+
- Add junit support. Use `--junit <filename>` to generate junit xml.
14+
15+
916
## [0.5.2] - 2022-06-16
1017

1118
### Fixed

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqllogictest"
3-
version = "0.5.2"
3+
version = "0.5.3"
44
edition = "2021"
55
description = "Sqllogictest parser and runner."
66
license = "MIT OR Apache-2.0"
@@ -11,7 +11,7 @@ keywords = ["sql", "database", "parser", "cli"]
1111
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
1212

1313
[features]
14-
bin = ["anyhow", "console", "tokio-postgres", "env_logger", "clap", "tokio", "futures"]
14+
bin = ["anyhow", "console", "tokio-postgres", "env_logger", "clap", "tokio", "futures", "quick-junit"]
1515

1616
[dependencies]
1717
anyhow = { version = "1", optional = true }
@@ -26,6 +26,7 @@ glob = "0.3"
2626
humantime = "2"
2727
itertools = "0.10"
2828
log = "0.4"
29+
quick-junit = { version = "0.2", optional = true }
2930
tempfile = "3"
3031
thiserror = "1"
3132
tokio = { version = "1", features = [

src/main.rs

Lines changed: 63 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,15 @@ use std::collections::BTreeMap;
22
use std::io::{stdout, Write};
33
use std::path::{Path, PathBuf};
44
use std::sync::Arc;
5-
use std::time::Instant;
5+
use std::time::{Duration, Instant};
66

77
use anyhow::{anyhow, Context, Result};
88
use async_trait::async_trait;
99
use clap::{ArgEnum, Parser};
1010
use console::style;
1111
use futures::StreamExt;
1212
use itertools::Itertools;
13+
use quick_junit::{NonSuccessKind, Report, TestCase, TestCaseStatus, TestSuite};
1314
use sqllogictest::{Control, Record, TestErrorKind};
1415

1516
#[derive(Copy, Clone, Debug, PartialEq, ArgEnum)]
@@ -58,6 +59,7 @@ struct Opt {
5859
#[clap(short = 'w', long, default_value = "postgres")]
5960
pass: String,
6061

62+
/// Whether to enable colorful output.
6163
#[clap(
6264
long,
6365
arg_enum,
@@ -71,6 +73,10 @@ struct Opt {
7173
/// database will be created for each test file.
7274
#[clap(long, short)]
7375
jobs: Option<usize>,
76+
77+
/// Report to junit XML.
78+
#[clap(long)]
79+
junit: Option<String>,
7480
}
7581

7682
#[tokio::main]
@@ -120,7 +126,14 @@ async fn main() -> Result<()> {
120126
return Err(anyhow!("no test case found"));
121127
}
122128

123-
if let Some(job) = &opt.jobs {
129+
let mut report = Report::new(
130+
opt.junit
131+
.clone()
132+
.unwrap_or_else(|| "sqllogictest".to_string()),
133+
);
134+
let mut test_suite = TestSuite::new("sqllogictest");
135+
136+
let result = if let Some(job) = &opt.jobs {
124137
let mut create_databases = BTreeMap::new();
125138
for file in files {
126139
let db_name = file
@@ -170,11 +183,20 @@ async fn main() -> Result<()> {
170183
let start = Instant::now();
171184

172185
while let Some((file, res, mut buf)) = stream.next().await {
173-
if let Err(e) = res {
174-
writeln!(buf, "{}\n\n{:?}", style("[FAILED]").red().bold(), e)?;
175-
writeln!(buf)?;
176-
failed_case.push(file);
177-
}
186+
let case = match res {
187+
Ok(duration) => {
188+
let mut case = TestCase::new(file, TestCaseStatus::success());
189+
case.set_time(duration);
190+
case
191+
}
192+
Err(e) => {
193+
writeln!(buf, "{}\n\n{:?}", style("[FAILED]").red().bold(), e)?;
194+
writeln!(buf)?;
195+
failed_case.push(file.clone());
196+
TestCase::new(file, TestCaseStatus::non_success(NonSuccessKind::Failure))
197+
}
198+
};
199+
test_suite.add_test_case(case);
178200
tokio::task::block_in_place(|| stdout().write_all(&buf))?;
179201
}
180202

@@ -184,7 +206,7 @@ async fn main() -> Result<()> {
184206
);
185207

186208
if !failed_case.is_empty() {
187-
Err(anyhow!("some test case failed:\n{:#?}", failed_case))
209+
return Err(anyhow!("some test case failed:\n{:#?}", failed_case));
188210
} else {
189211
Ok(())
190212
}
@@ -194,19 +216,40 @@ async fn main() -> Result<()> {
194216
let mut failed_case = vec![];
195217

196218
for file in files {
197-
if let Err(e) = run_test_file(&mut std::io::stdout(), pg.clone(), &file).await {
198-
println!("{}\n\n{:?}", style("[FAILED]").red().bold(), e);
199-
println!();
200-
failed_case.push(file.to_string_lossy().to_string());
201-
}
219+
let filename = file.to_string_lossy().to_string();
220+
let case = match run_test_file(&mut std::io::stdout(), pg.clone(), &file).await {
221+
Ok(duration) => {
222+
let mut case = TestCase::new(filename, TestCaseStatus::success());
223+
case.set_time(duration);
224+
case
225+
}
226+
Err(e) => {
227+
println!("{}\n\n{:?}", style("[FAILED]").red().bold(), e);
228+
println!();
229+
failed_case.push(filename.clone());
230+
TestCase::new(
231+
filename,
232+
TestCaseStatus::non_success(NonSuccessKind::Failure),
233+
)
234+
}
235+
};
236+
test_suite.add_test_case(case);
202237
}
203238

204239
if !failed_case.is_empty() {
205240
Err(anyhow!("some test case failed:\n{:#?}", failed_case))
206241
} else {
207242
Ok(())
208243
}
244+
};
245+
246+
report.add_test_suite(test_suite);
247+
248+
if let Some(junit_file) = opt.junit {
249+
tokio::fs::write(format!("{}-junit.xml", junit_file), report.to_string()?).await?;
209250
}
251+
252+
result
210253
}
211254

212255
async fn flush(out: &mut impl std::io::Write) -> std::io::Result<()> {
@@ -218,7 +261,7 @@ async fn run_test_file_on_db(
218261
filename: PathBuf,
219262
db_name: String,
220263
opt: Opt,
221-
) -> Result<()> {
264+
) -> Result<Duration> {
222265
let (client, connection) = tokio_postgres::Config::new()
223266
.host(&opt.host)
224267
.port(opt.port)
@@ -240,18 +283,18 @@ async fn run_test_file_on_db(
240283
engine_name: opt.engine.clone(),
241284
};
242285

243-
run_test_file(out, pg, filename).await?;
286+
let result = run_test_file(out, pg, filename).await?;
244287

245288
handle.abort();
246289

247-
Ok(())
290+
Ok(result)
248291
}
249292

250293
async fn run_test_file<T: std::io::Write>(
251294
out: &mut T,
252295
engine: Postgres,
253296
filename: impl AsRef<Path>,
254-
) -> Result<()> {
297+
) -> Result<Duration> {
255298
let filename = filename.as_ref();
256299
let mut runner = sqllogictest::Runner::new(engine);
257300
let records = tokio::task::block_in_place(|| {
@@ -341,6 +384,8 @@ async fn run_test_file<T: std::io::Write>(
341384
))?;
342385
}
343386

387+
let duration = begin_times[0].elapsed();
388+
344389
finish(
345390
out,
346391
&mut begin_times,
@@ -350,7 +395,7 @@ async fn run_test_file<T: std::io::Write>(
350395

351396
writeln!(out)?;
352397

353-
Ok(())
398+
Ok(duration)
354399
}
355400

356401
#[derive(Clone)]

src/parser.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
//! Sqllogictest parser.
22
3-
use crate::ParseErrorKind::InvalidIncludeFile;
43
use std::fmt;
54
use std::path::Path;
65
use std::sync::Arc;
76
use std::time::Duration;
87

8+
use crate::ParseErrorKind::InvalidIncludeFile;
9+
910
/// The location in source file.
1011
#[derive(Debug, PartialEq, Eq, Clone)]
1112
pub struct Location {

0 commit comments

Comments
 (0)