Skip to content

Commit a485002

Browse files
authored
feat: new test ui (risinglightdb#32)
Signed-off-by: Alex Chi <[email protected]>
1 parent f66d9f7 commit a485002

File tree

11 files changed

+188
-69
lines changed

11 files changed

+188
-69
lines changed

.github/workflows/ci.yml

+7-2
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,18 @@ jobs:
1919
- uses: actions-rs/toolchain@v1
2020
with:
2121
profile: minimal
22-
toolchain: stable
22+
toolchain: nightly
2323
components: rustfmt, clippy
2424
- name: Check code format
2525
uses: actions-rs/cargo@v1
2626
with:
2727
command: fmt
2828
args: --all -- --check
29+
- name: Clippy
30+
uses: actions-rs/cargo@v1
31+
with:
32+
command: clippy
33+
args: --all-targets -- -D warnings
2934
- name: Clippy
3035
uses: actions-rs/cargo@v1
3136
with:
@@ -39,7 +44,7 @@ jobs:
3944
- uses: actions-rs/toolchain@v1
4045
with:
4146
profile: minimal
42-
toolchain: stable
47+
toolchain: nightly
4348
- name: Build
4449
uses: actions-rs/cargo@v1
4550
with:

.github/workflows/release.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
- uses: actions-rs/toolchain@v1
1818
with:
1919
profile: minimal
20-
toolchain: stable
20+
toolchain: nightly
2121
components: rustfmt, clippy
2222
- uses: actions-rs/cargo@v1
2323
name: Compile all targets 🚀

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
/target
22
Cargo.lock
33
.vscode/
4+
e2e_test/

CHANGELOG.md

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
66

77
## [Unreleased]
88

9+
### Added
10+
11+
- New test UI for sqllogictest binary
12+
913
## [0.3.0] - 2022-03-21
1014

1115
### Added

Cargo.toml

+12-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqllogictest"
3-
version = "0.3.0"
3+
version = "0.3.1"
44
edition = "2021"
55
description = "Sqllogictest parser and runner."
66
license = "MIT OR Apache-2.0"
@@ -11,19 +11,27 @@ 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 = ["postgres", "env_logger", "glob", "clap", "libtest-mimic"]
14+
bin = ["anyhow", "console", "tokio-postgres", "env_logger", "glob", "clap", "tokio"]
1515

1616
[dependencies]
17+
anyhow = { version = "1", optional = true }
1718
async-trait = "0.1"
1819
clap = { version = "3", features = ["derive"], optional = true }
20+
console = { version = "0.15", optional = true }
1921
env_logger = { version = "0.9", optional = true }
2022
futures-lite = "1"
2123
glob = { version = "0.3", optional = true }
2224
humantime = "2"
2325
itertools = "0.10"
24-
libtest-mimic = { version = "0.3", optional = true }
2526
log = "0.4"
26-
postgres = { version = "0.19", optional = true }
27+
tokio = { version = "1", features = [
28+
"rt",
29+
"rt-multi-thread",
30+
"sync",
31+
"macros",
32+
"fs"
33+
], optional = true }
34+
tokio-postgres = { version = "0.7", optional = true }
2735
tempfile = "3"
2836
thiserror = "1"
2937

examples/file_level_sort_mode.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ impl sqllogictest::DB for FakeDB {
1818

1919
fn run(&mut self, sql: &str) -> Result<String, FakeDBError> {
2020
if sql == "select * from example_file_level_sort_mode" {
21-
// Even if the order is not the same as `slt` file, sqllogictest will sort them before comparing.
21+
// Even if the order is not the same as `slt` file, sqllogictest will sort them before
22+
// comparing.
2223
return Ok("1 10 2333\n2 20 2333\n10 100 2333".into());
2324
}
2425
unimplemented!("unsupported SQL: {}", sql);

examples/rowsort.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,8 @@ impl sqllogictest::DB for FakeDB {
1818

1919
fn run(&mut self, sql: &str) -> Result<String, FakeDBError> {
2020
if sql == "select * from example_rowsort" {
21-
// Even if the order is not the same as `slt` file, sqllogictest will sort them before comparing.
21+
// Even if the order is not the same as `slt` file, sqllogictest will sort them before
22+
// comparing.
2223
return Ok("1 10 2333\n2 20 2333\n10 100 2333".into());
2324
}
2425
unimplemented!("unsupported SQL: {}", sql);

rustfmt.toml

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
comment_width = 120
2+
format_code_in_doc_comments = true
3+
format_macro_bodies = true
4+
format_macro_matchers = true
5+
normalize_comments = true
6+
normalize_doc_attributes = true
7+
imports_granularity = "Module"
8+
group_imports = "StdExternalCrate"
9+
reorder_imports = true
10+
tab_spaces = 4
11+
wrap_comments = true

src/main.rs

+118-40
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
1+
use std::io::{stdout, Write};
2+
use std::path::Path;
3+
use std::sync::Arc;
4+
use std::time::Instant;
5+
6+
use anyhow::{anyhow, Context, Result};
7+
use async_trait::async_trait;
18
use clap::Parser;
2-
use libtest_mimic::{run_tests, Arguments, Outcome, Test};
3-
use std::sync::{Arc, Mutex};
9+
use console::style;
10+
use sqllogictest::{Control, Record};
411

512
#[derive(Parser, Debug)]
613
#[clap(about, version, author)]
@@ -35,66 +42,137 @@ struct Opt {
3542
pass: String,
3643
}
3744

38-
fn main() {
45+
#[tokio::main]
46+
async fn main() -> Result<()> {
3947
env_logger::init();
4048

4149
let opt = Opt::parse();
4250

4351
let files = glob::glob(&opt.files).expect("failed to read glob pattern");
44-
let client = postgres::Config::new()
52+
53+
let (client, connection) = tokio_postgres::Config::new()
4554
.host(&opt.host)
4655
.port(opt.port)
4756
.dbname(&opt.db)
4857
.user(&opt.user)
4958
.password(&opt.pass)
50-
.connect(postgres::NoTls)
51-
.expect("failed to connect to postgres");
59+
.connect(tokio_postgres::NoTls)
60+
.await
61+
.context("failed to connect to postgres")?;
62+
63+
tokio::spawn(async move {
64+
if let Err(e) = connection.await {
65+
log::error!("Postgres connection error: {:?}", e);
66+
}
67+
});
5268

5369
let pg = Postgres {
54-
client: Arc::new(Mutex::new(client)),
70+
client: Arc::new(client),
5571
engine_name: opt.engine,
5672
};
57-
let tests = files
58-
.map(|file| Test {
59-
name: file.unwrap().to_str().unwrap().into(),
60-
kind: String::new(),
61-
is_ignored: false,
62-
is_bench: false,
63-
data: pg.clone(),
64-
})
65-
.collect();
66-
67-
// Parse command line arguments
68-
let mut args = Arguments::from_iter(std::env::args().take(1));
69-
args.num_threads = Some(1);
70-
71-
// Run all tests and exit the application appropriatly (in this case, the
72-
// test runner is a dummy runner which does nothing and says that all tests
73-
// passed).
74-
run_tests(&args, tests, run_test).exit();
73+
74+
for file in files {
75+
let file = file?;
76+
if let Err(e) = run_test_file(pg.clone(), &file).await {
77+
println!("{}\n\n{:?}", style("[FAILED]").red().bold(), e);
78+
println!();
79+
}
80+
}
81+
82+
Ok(())
7583
}
7684

77-
fn run_test(test: &Test<Postgres>) -> Outcome {
78-
let mut runner = sqllogictest::Runner::new(test.data.clone());
79-
match runner.run_file(&test.name) {
80-
Ok(_) => Outcome::Passed,
81-
Err(err) => Outcome::Failed {
82-
msg: Some(err.to_string()),
83-
},
85+
async fn flush_stdout() -> std::io::Result<()> {
86+
tokio::task::block_in_place(|| stdout().flush())
87+
}
88+
89+
async fn run_test_file(engine: Postgres, filename: impl AsRef<Path>) -> Result<()> {
90+
let filename = filename.as_ref();
91+
let mut runner = sqllogictest::Runner::new(engine);
92+
let records = tokio::task::block_in_place(|| {
93+
sqllogictest::parse_file(&filename).map_err(|e| anyhow!("{:?}", e))
94+
})
95+
.context("failed to parse sqllogictest file")?;
96+
97+
let mut begin_times = vec![];
98+
let mut did_pop = false;
99+
100+
print!("{} .. ", filename.to_string_lossy());
101+
flush_stdout().await?;
102+
103+
begin_times.push(Instant::now());
104+
105+
let finish = |time_stack: &mut Vec<Instant>, did_pop: &mut bool, file: &str| {
106+
let begin_time = time_stack.pop().unwrap();
107+
108+
if *did_pop {
109+
// start a new line if the result is not immediately after the item
110+
print!(
111+
"\n{}{} {} {} in {} ms",
112+
" ".repeat(time_stack.len()),
113+
file,
114+
style("[END]").blue().bold(),
115+
style("[OK]").green().bold(),
116+
begin_time.elapsed().as_millis()
117+
);
118+
} else {
119+
// otherwise, append time to the previous line
120+
print!(
121+
"{} in {} ms",
122+
style("[OK]").green().bold(),
123+
begin_time.elapsed().as_millis()
124+
);
125+
}
126+
127+
*did_pop = true;
128+
};
129+
130+
for record in records {
131+
match &record {
132+
Record::Control(Control::BeginInclude(file)) => {
133+
begin_times.push(Instant::now());
134+
if !did_pop {
135+
println!("{}", style("[BEGIN]").blue().bold());
136+
} else {
137+
println!();
138+
}
139+
did_pop = false;
140+
print!("{}{} .. ", " ".repeat(begin_times.len() - 1), file);
141+
flush_stdout().await?;
142+
}
143+
Record::Control(Control::EndInclude(file)) => {
144+
finish(&mut begin_times, &mut did_pop, file);
145+
}
146+
_ => {}
147+
}
148+
runner
149+
.run_async(record)
150+
.await
151+
.map_err(|e| anyhow!("{:?}", e))
152+
.context(format!(
153+
"failed to run `{}`",
154+
style(filename.to_string_lossy()).bold()
155+
))?;
84156
}
157+
158+
finish(&mut begin_times, &mut did_pop, &*filename.to_string_lossy());
159+
160+
println!();
161+
162+
Ok(())
85163
}
86164

87165
#[derive(Clone)]
88166
struct Postgres {
89-
client: Arc<Mutex<postgres::Client>>,
90-
167+
client: Arc<tokio_postgres::Client>,
91168
engine_name: String,
92169
}
93170

94-
impl sqllogictest::DB for Postgres {
95-
type Error = postgres::error::Error;
171+
#[async_trait]
172+
impl sqllogictest::AsyncDB for Postgres {
173+
type Error = tokio_postgres::error::Error;
96174

97-
fn run(&mut self, sql: &str) -> Result<String, Self::Error> {
175+
async fn run(&mut self, sql: &str) -> Result<String, Self::Error> {
98176
use std::fmt::Write;
99177

100178
let mut output = String::new();
@@ -104,10 +182,10 @@ impl sqllogictest::DB for Postgres {
104182
// and we have to follow the format given by the specific database (pg).
105183
// For example, postgres will output `t` as true and `f` as false,
106184
// thus we have to write `t`/`f` in the expected results.
107-
let rows = self.client.lock().unwrap().simple_query(sql)?;
185+
let rows = self.client.simple_query(sql).await?;
108186
for row in rows {
109187
match row {
110-
postgres::SimpleQueryMessage::Row(row) => {
188+
tokio_postgres::SimpleQueryMessage::Row(row) => {
111189
for i in 0..row.len() {
112190
if i != 0 {
113191
write!(output, " ").unwrap();
@@ -118,7 +196,7 @@ impl sqllogictest::DB for Postgres {
118196
}
119197
}
120198
}
121-
postgres::SimpleQueryMessage::CommandComplete(_) => {}
199+
tokio_postgres::SimpleQueryMessage::CommandComplete(_) => {}
122200
_ => unreachable!(),
123201
}
124202
writeln!(output).unwrap();

0 commit comments

Comments
 (0)