Skip to content

Commit 992383c

Browse files
yliang412skyzh
andauthored
improve individual test matching and output (#9)
* improve individual test matching and output Signed-off-by: Yuchen Liang <[email protected]> * newline at end of test files Signed-off-by: Yuchen Liang <[email protected]> * apply clippy fixes Signed-off-by: Yuchen Liang <[email protected]> * add tasks Signed-off-by: Alex Chi <[email protected]> * piggy-back version bump to 0.4 Signed-off-by: Alex Chi <[email protected]> --------- Signed-off-by: Yuchen Liang <[email protected]> Signed-off-by: Alex Chi <[email protected]> Co-authored-by: Alex Chi <[email protected]>
1 parent f464886 commit 992383c

File tree

10 files changed

+133
-21
lines changed

10 files changed

+133
-21
lines changed

Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "sqlplannertest"
3-
version = "0.3.0"
3+
version = "0.4.0"
44
edition = "2021"
55
description = "A yaml-based SQL planner test framework."
66
license = "MIT OR Apache-2.0"
@@ -16,6 +16,7 @@ async-trait = "0.1"
1616
console = "0.15"
1717
futures-util = { version = "0.3", default-features = false, features = ["alloc"] }
1818
glob = "0.3"
19+
itertools = "0.13"
1920
libtest-mimic = "0.7"
2021
serde = { version = "1.0", features = ["derive"] }
2122
serde_yaml = "0.9"

naivedb/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ edition = "2021"
77
[dependencies]
88
anyhow = { version = "1", features = ["backtrace"] }
99
async-trait = "0.1"
10+
clap = { version = "4.5", features = ["derive"] }
1011
sqlplannertest = { path = ".." }
1112
tokio = { version = "1", features = ["rt", "rt-multi-thread", "macros", "fs"] }
1213

naivedb/src/bin/apply.rs

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,31 @@
11
use std::path::Path;
22

33
use anyhow::Result;
4+
use clap::Parser;
5+
use sqlplannertest::PlannerTestApplyOptions;
6+
7+
#[derive(Parser)]
8+
#[command(version, about, long_about = None)]
9+
struct Cli {
10+
/// Optional list of selections to apply the test; if empty, apply all tests
11+
selections: Vec<String>,
12+
/// Execute tests in serial
13+
#[clap(long)]
14+
serial: bool,
15+
}
416

517
#[tokio::main]
618
async fn main() -> Result<()> {
7-
sqlplannertest::planner_test_apply(
19+
let cli = Cli::parse();
20+
21+
let options = PlannerTestApplyOptions {
22+
serial: cli.serial,
23+
selections: cli.selections,
24+
};
25+
sqlplannertest::planner_test_apply_with_options(
826
Path::new(env!("CARGO_MANIFEST_DIR")).join("tests"),
927
|| async { Ok(naivedb::NaiveDb::new()) },
28+
options,
1029
)
1130
.await?;
1231
Ok(())
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
-- Test whether select stmt works correctly.
2+
CREATE TABLE t1(v1 int);
3+
4+
/*
5+
=== logical
6+
I'm a naive db, so I don't now how to process
7+
CREATE TABLE t1(v1 int);
8+
9+
10+
=== physical
11+
I'm a naive db, so I don't now how to process
12+
CREATE TABLE t1(v1 int);
13+
*/
14+

naivedb/tests/basics/create.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
- sql: |
2+
CREATE TABLE t1(v1 int);
3+
desc: Test whether select stmt works correctly.
4+
tasks:
5+
- logical
6+
- physical
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
-- Test whether select stmt works correctly.
2+
SELECT * FROM t1;
3+
4+
/*
5+
=== logical
6+
I'm a naive db, so I don't now how to process
7+
CREATE TABLE t1(v1 int);
8+
SELECT * FROM t1;
9+
10+
11+
=== physical
12+
I'm a naive db, so I don't now how to process
13+
CREATE TABLE t1(v1 int);
14+
SELECT * FROM t1;
15+
*/
16+

naivedb/tests/basics/select.yml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
- sql: |
2+
SELECT * FROM t1;
3+
desc: Test whether select stmt works correctly.
4+
before: ["CREATE TABLE t1(v1 int);"]
5+
tasks:
6+
- logical
7+
- physical

src/apply.rs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@ use console::style;
77
use futures_util::{stream, StreamExt, TryFutureExt};
88

99
use crate::{
10-
discover_tests, parse_test_cases, ParsedTestCase, PlannerTestRunner, TestCase, RESULT_SUFFIX,
10+
discover_tests_with_selections, parse_test_cases, ParsedTestCase, PlannerTestRunner, TestCase,
11+
RESULT_SUFFIX,
1112
};
1213

1314
#[derive(Default, Clone)]
1415
pub struct PlannerTestApplyOptions {
1516
pub serial: bool,
17+
/// A selection of test modules or files.
18+
pub selections: Vec<String>,
1619
}
1720

1821
pub async fn planner_test_apply<F, Ft, R>(path: impl AsRef<Path>, runner_fn: F) -> Result<()>
@@ -25,7 +28,7 @@ where
2528
}
2629

2730
pub async fn planner_test_apply_with_options<F, Ft, R>(
28-
path: impl AsRef<Path>,
31+
tests_dir: impl AsRef<Path>,
2932
runner_fn: F,
3033
options: PlannerTestApplyOptions,
3134
) -> Result<()>
@@ -34,14 +37,14 @@ where
3437
Ft: Future<Output = Result<R>> + Send,
3538
R: PlannerTestRunner + 'static,
3639
{
37-
let tests = discover_tests(path)?
40+
let tests = discover_tests_with_selections(&tests_dir, &options.selections)?
3841
.map(|path| {
3942
let path = path?;
40-
let filename = path
41-
.file_name()
42-
.context("unable to extract filename")?
43-
.to_os_string();
44-
let testname = filename
43+
let relative_path = path
44+
.strip_prefix(&tests_dir)
45+
.context("unable to relative path")?
46+
.as_os_str();
47+
let testname = relative_path
4548
.to_str()
4649
.context("unable to convert to string")?
4750
.to_string();

src/lib.rs

Lines changed: 46 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ use anyhow::{Context, Result};
88
pub use apply::{planner_test_apply, planner_test_apply_with_options, PlannerTestApplyOptions};
99
use async_trait::async_trait;
1010
use glob::Paths;
11+
use itertools::Itertools;
1112
use resolve_id::resolve_testcase_id;
1213
use serde::{Deserialize, Serialize};
1314
pub use test_runner::planner_test_runner;
@@ -51,12 +52,51 @@ pub fn parse_test_cases(
5152
const TEST_SUFFIX: &str = ".yml";
5253
const RESULT_SUFFIX: &str = "planner.sql";
5354

54-
pub fn discover_tests(path: impl AsRef<Path>) -> Result<Paths> {
55-
let pattern = format!("**/[!_]*{}", TEST_SUFFIX);
56-
let path = path.as_ref().join(pattern);
57-
let path = path.to_str().context("non utf-8 path")?;
58-
let paths = glob::glob(path).context("failed to discover test")?;
59-
Ok(paths)
55+
pub fn discover_tests(path: impl AsRef<Path>) -> Result<impl Iterator<Item = glob::GlobResult>> {
56+
discover_tests_with_selections(path, &[])
57+
}
58+
59+
pub fn discover_tests_with_selections(
60+
path: impl AsRef<Path>,
61+
selections: &[String],
62+
) -> Result<impl Iterator<Item = glob::GlobResult>> {
63+
let patterns = mk_patterns(&path, selections);
64+
let paths: Vec<Paths> = patterns
65+
.into_iter()
66+
.map(|pattern| glob::glob(&pattern).context("input pattern is invalid"))
67+
.try_collect()?;
68+
69+
Ok(paths.into_iter().flatten())
70+
}
71+
72+
/// Make glob patterns based on `selections`.
73+
///
74+
/// If `selections` is empty, returns the glob pattern that select all tests within `path`.
75+
/// Otherwise returns the glob pattterns that matches the selected test modules or files.
76+
fn mk_patterns(path: impl AsRef<Path>, selections: &[String]) -> Vec<String> {
77+
let mk_pattern = |glob: String| {
78+
let path = path.as_ref().join(glob);
79+
path.to_str().expect("non utf-8 path").to_string()
80+
};
81+
82+
if selections.is_empty() {
83+
// Select all tests
84+
return vec![mk_pattern(format!("**/[!_]*{}", TEST_SUFFIX))];
85+
}
86+
87+
// Select matching tests.
88+
selections
89+
.iter()
90+
.flat_map(|s| {
91+
let path_segment = s.replace("::", "/");
92+
// e.g. tests/<..>/path_segment.yml
93+
let file_match = mk_pattern(format!("**/{path_segment}{}", TEST_SUFFIX));
94+
// Module match, needs to start at the top level.
95+
// e.g. tests/path_segment/<..>/<some>.yml
96+
let module_match = mk_pattern(format!("{path_segment}/**/[!_]*{}", TEST_SUFFIX));
97+
std::iter::once(file_match).chain(std::iter::once(module_match))
98+
})
99+
.collect()
60100
}
61101

62102
#[cfg(test)]

src/test_runner.rs

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,26 @@ impl fmt::Display for Line {
2727
// End Copyright 2022 Armin Ronacher
2828

2929
/// Test runner based on libtest-mimic.
30-
pub fn planner_test_runner<F, Ft, R>(path: impl AsRef<Path>, runner_fn: F) -> Result<()>
30+
pub fn planner_test_runner<F, Ft, R>(tests_dir: impl AsRef<Path>, runner_fn: F) -> Result<()>
3131
where
3232
F: Fn() -> Ft + Send + Sync + 'static + Clone,
3333
Ft: Future<Output = Result<R>> + Send,
3434
R: PlannerTestRunner,
3535
{
36-
let paths = discover_tests(path)?;
36+
let paths = discover_tests(&tests_dir)?;
3737

3838
let args = Arguments::from_args();
3939

4040
let mut tests = vec![];
4141
for entry in paths {
4242
let path = entry.context("failed to read glob entry")?;
43-
let filename = path.file_name().context("unable to extract filename")?;
44-
let testname = filename.to_str().context("unable to convert to string")?;
43+
let relative_path = path
44+
.strip_prefix(&tests_dir)
45+
.context("failed to extract relative path")?
46+
.as_os_str();
47+
let testname = relative_path
48+
.to_str()
49+
.context("unable to convert to string")?;
4550

4651
let nocapture = args.nocapture;
4752
let runner_fn = runner_fn.clone();
@@ -50,7 +55,7 @@ where
5055
testname
5156
.strip_suffix(TEST_SUFFIX)
5257
.unwrap()
53-
.replace('/', "_"),
58+
.replace('/', "::"),
5459
move || run(path, nocapture, runner_fn),
5560
));
5661
}

0 commit comments

Comments
 (0)