diff --git a/src/cmd/sql_test.rs b/src/cmd/sql_test.rs index 02437b6..9fbe04b 100644 --- a/src/cmd/sql_test.rs +++ b/src/cmd/sql_test.rs @@ -12,7 +12,7 @@ use regex::Regex; use tracing::instrument; use crate::{ - ast::{self, parse_sql, CreateTableStatement, CreateViewStatement}, + ast::{self, parse_sql, CreateTableStatement, CreateViewStatement, Target}, drivers::{self, Driver}, errors::{format_err, Context, Error, Result}, }; @@ -42,7 +42,7 @@ pub async fn cmd_sql_test(opt: &SqlTestOpt) -> Result<()> { // Keep track of our test results. let mut test_count = 0usize; let mut test_failures: Vec<(PathBuf, Error)> = vec![]; - let mut pending: Vec<(PathBuf, String)> = vec![]; + let mut pending: Vec = vec![]; // Build a glob matching our test files, for use with `glob`. let dir_path_str = opt.dir_path.as_os_str().to_str().ok_or_else(|| { @@ -72,26 +72,14 @@ pub async fn cmd_sql_test(opt: &SqlTestOpt) -> Result<()> { // Skip pending tests unless asked to run them. if !opt.pending { - // Look for lines of the form `-- pending: db1 Comment`. - static PENDING_RE: Lazy = - Lazy::new(|| Regex::new(r"(?m)^--\s*pending:\s*([a-zA-Z0-9_]+)(\s+.*)?").unwrap()); - let target_string = driver.target().to_string(); - if let Some(caps) = PENDING_RE.captures(&query) { - let db = caps.get(1).unwrap().as_str(); - let comment = caps.get(2).map_or("", |m| m.as_str().trim()); - if db == target_string { - print!("P"); - let _ = io::stdout().flush(); - - pending.push(( - path.strip_prefix(&base_dir) - .unwrap_or_else(|_| &path) - .to_owned(), - comment.to_owned(), - )); - - continue; - } + let short_path = path.strip_prefix(&base_dir).unwrap_or(&path); + if let Some(pending_test_info) = + PendingTestInfo::for_target(driver.target(), short_path, &query) + { + pending.push(pending_test_info); + print!("P"); + let _ = io::stdout().flush(); + continue; } } @@ -117,8 +105,8 @@ pub async fn cmd_sql_test(opt: &SqlTestOpt) -> Result<()> { if !pending.is_empty() { println!("\nPending tests:"); - for (path, comment) in &pending { - println!(" {} ({})", path.display(), comment); + for p in &pending { + println!(" {} ({})", p.path.display(), p.comment); } } @@ -178,6 +166,42 @@ async fn run_test( Ok(()) } +/// Information on a pending test. +struct PendingTestInfo { + path: PathBuf, + database: String, + comment: String, +} + +impl PendingTestInfo { + /// Find the `PendingTestInfo` for a given database. + fn for_target(target: Target, path: &Path, sql: &str) -> Option { + let database = target.to_string(); + Self::all_from_test(path, sql) + .into_iter() + .find(|info| info.database == database) + } + + /// Find all `pending:` lines in a test file. + fn all_from_test(path: &Path, sql: &str) -> Vec { + // Look for lines of the form `-- pending: db1 Comment`. + static PENDING_RE: Lazy = + Lazy::new(|| Regex::new(r"(?m)^--\s*pending:\s*([a-zA-Z0-9_]+)(\s+.*)?").unwrap()); + PENDING_RE + .captures_iter(sql) + .map(|cap| { + let database = cap.get(1).unwrap().as_str(); + let comment = cap.get(2).map_or("", |m| m.as_str().trim()); + PendingTestInfo { + path: path.to_owned(), + database: database.to_owned(), + comment: comment.to_owned(), + } + }) + .collect() + } +} + /// Tables output by a test suite. This normally stores `ast::TableName`s, but /// we use `Option` while extracting the table names from the AST. #[derive(Clone, Debug, Default)] diff --git a/src/drivers/sqlite3/mod.rs b/src/drivers/sqlite3/mod.rs index 9301eb4..9a94f72 100644 --- a/src/drivers/sqlite3/mod.rs +++ b/src/drivers/sqlite3/mod.rs @@ -114,6 +114,8 @@ impl SQLite3Driver { ("format_datetime", 2), ("generate_uuid", 0), ("interval", 2), + ("regexp_extract", 2), + ("regexp_replace", 3), ("struct", -1), ("function.custom", 0), ];