Skip to content

Commit c976a89

Browse files
committed
Extract useful methods from sqllogictest bin (apache#14267)
1 parent f3e7004 commit c976a89

File tree

3 files changed

+161
-59
lines changed

3 files changed

+161
-59
lines changed

datafusion/sqllogictest/bin/sqllogictests.rs

Lines changed: 50 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,32 @@
1515
// specific language governing permissions and limitations
1616
// under the License.
1717

18+
use clap::Parser;
19+
use datafusion_common::instant::Instant;
20+
use datafusion_common::utils::get_available_parallelism;
21+
use datafusion_common::{exec_err, DataFusionError, Result};
22+
use datafusion_common_runtime::SpawnedTask;
23+
use datafusion_sqllogictest::{
24+
df_value_validator, read_dir_recursive, setup_scratch_dir, value_normalizer,
25+
DataFusion, TestContext,
26+
};
27+
use futures::stream::StreamExt;
28+
use indicatif::{
29+
HumanDuration, MultiProgress, ProgressBar, ProgressDrawTarget, ProgressStyle,
30+
};
31+
use itertools::Itertools;
32+
use log::Level::Info;
33+
use log::{info, log_enabled};
34+
use sqllogictest::{
35+
parse_file, strict_column_validator, AsyncDB, Condition, Normalizer, Record,
36+
Validator,
37+
};
38+
39+
#[cfg(feature = "postgres")]
40+
use crate::postgres_container::{
41+
initialize_postgres_container, terminate_postgres_container,
42+
};
1843
use std::ffi::OsStr;
19-
use std::fs;
2044
use std::path::{Path, PathBuf};
2145

2246
use clap::Parser;
@@ -40,39 +64,33 @@ pub fn main() -> Result<()> {
4064
.block_on(run_tests())
4165
}
4266

43-
fn value_validator(actual: &[Vec<String>], expected: &[String]) -> bool {
44-
let expected = expected
67+
fn sqlite_value_validator(
68+
normalizer: Normalizer,
69+
actual: &[Vec<String>],
70+
expected: &[String],
71+
) -> bool {
72+
let normalized_expected = expected.iter().map(normalizer).collect::<Vec<_>>();
73+
let normalized_actual = actual
4574
.iter()
46-
// Trailing whitespace from lines in SLT will typically be removed, but do not fail if it is not
47-
// If particular test wants to cover trailing whitespace on a value,
48-
// it should project additional non-whitespace column on the right.
49-
.map(|s| s.trim_end().to_owned())
50-
.collect::<Vec<_>>();
51-
let actual = actual
52-
.iter()
53-
.map(|strs| strs.iter().join(" "))
54-
// Editors do not preserve trailing whitespace, so expected may or may not lack it included
55-
.map(|s| s.trim_end().to_owned())
56-
.collect::<Vec<_>>();
57-
actual == expected
58-
}
59-
60-
/// Sets up an empty directory at test_files/scratch/<name>
61-
/// creating it if needed and clearing any file contents if it exists
62-
/// This allows tests for inserting to external tables or copy to
63-
/// to persist data to disk and have consistent state when running
64-
/// a new test
65-
fn setup_scratch_dir(name: &Path) -> Result<()> {
66-
// go from copy.slt --> copy
67-
let file_stem = name.file_stem().expect("File should have a stem");
68-
let path = PathBuf::from("test_files").join("scratch").join(file_stem);
69-
70-
info!("Creating scratch dir in {path:?}");
71-
if path.exists() {
72-
fs::remove_dir_all(&path)?;
75+
.map(|strs| strs.iter().map(normalizer).join(" "))
76+
.collect_vec();
77+
78+
if log_enabled!(Info) && normalized_actual != normalized_expected {
79+
info!("sqlite validation failed. actual vs expected:");
80+
for i in 0..normalized_actual.len() {
81+
info!("[{i}] {}<eol>", normalized_actual[i]);
82+
info!(
83+
"[{i}] {}<eol>",
84+
if normalized_expected.len() >= i {
85+
&normalized_expected[i]
86+
} else {
87+
"No more results"
88+
}
89+
);
90+
}
7391
}
74-
fs::create_dir_all(&path)?;
75-
Ok(())
92+
93+
normalized_actual == normalized_expected
7694
}
7795

7896
async fn run_tests() -> Result<()> {
@@ -275,33 +293,6 @@ fn read_test_files<'a>(
275293
))
276294
}
277295

278-
fn read_dir_recursive<P: AsRef<Path>>(path: P) -> Result<Vec<PathBuf>> {
279-
let mut dst = vec![];
280-
read_dir_recursive_impl(&mut dst, path.as_ref())?;
281-
Ok(dst)
282-
}
283-
284-
/// Append all paths recursively to dst
285-
fn read_dir_recursive_impl(dst: &mut Vec<PathBuf>, path: &Path) -> Result<()> {
286-
let entries = fs::read_dir(path)
287-
.map_err(|e| exec_datafusion_err!("Error reading directory {path:?}: {e}"))?;
288-
for entry in entries {
289-
let path = entry
290-
.map_err(|e| {
291-
exec_datafusion_err!("Error reading entry in directory {path:?}: {e}")
292-
})?
293-
.path();
294-
295-
if path.is_dir() {
296-
read_dir_recursive_impl(dst, &path)?;
297-
} else {
298-
dst.push(path);
299-
}
300-
}
301-
302-
Ok(())
303-
}
304-
305296
/// Parsed command line options
306297
///
307298
/// This structure attempts to mimic the command line options of the built in rust test runner

datafusion/sqllogictest/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,7 @@ pub use engines::DataFusion;
2828
pub use engines::Postgres;
2929

3030
mod test_context;
31+
mod util;
32+
3133
pub use test_context::TestContext;
34+
pub use util::*;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
// Licensed to the Apache Software Foundation (ASF) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The ASF licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
use datafusion_common::{exec_datafusion_err, Result};
19+
use itertools::Itertools;
20+
use log::Level::Warn;
21+
use log::{info, log_enabled, warn};
22+
use sqllogictest::Normalizer;
23+
use std::fs;
24+
use std::path::{Path, PathBuf};
25+
26+
/// Sets up an empty directory at `test_files/scratch/<name>`
27+
/// creating it if needed and clearing any file contents if it exists
28+
/// This allows tests for inserting to external tables or copy to
29+
/// persist data to disk and have consistent state when running
30+
/// a new test
31+
pub fn setup_scratch_dir(name: &Path) -> Result<()> {
32+
// go from copy.slt --> copy
33+
let file_stem = name.file_stem().expect("File should have a stem");
34+
let path = PathBuf::from("test_files").join("scratch").join(file_stem);
35+
36+
info!("Creating scratch dir in {path:?}");
37+
if path.exists() {
38+
fs::remove_dir_all(&path)?;
39+
}
40+
fs::create_dir_all(&path)?;
41+
Ok(())
42+
}
43+
44+
/// Trailing whitespace from lines in SLT will typically be removed, but do not fail if it is not
45+
/// If particular test wants to cover trailing whitespace on a value,
46+
/// it should project additional non-whitespace column on the right.
47+
#[allow(clippy::ptr_arg)]
48+
pub fn value_normalizer(s: &String) -> String {
49+
s.trim_end().to_string()
50+
}
51+
52+
pub fn read_dir_recursive<P: AsRef<Path>>(path: P) -> Result<Vec<PathBuf>> {
53+
let mut dst = vec![];
54+
read_dir_recursive_impl(&mut dst, path.as_ref())?;
55+
Ok(dst)
56+
}
57+
58+
/// Append all paths recursively to dst
59+
fn read_dir_recursive_impl(dst: &mut Vec<PathBuf>, path: &Path) -> Result<()> {
60+
let entries = fs::read_dir(path)
61+
.map_err(|e| exec_datafusion_err!("Error reading directory {path:?}: {e}"))?;
62+
for entry in entries {
63+
let path = entry
64+
.map_err(|e| {
65+
exec_datafusion_err!("Error reading entry in directory {path:?}: {e}")
66+
})?
67+
.path();
68+
69+
if path.is_dir() {
70+
read_dir_recursive_impl(dst, &path)?;
71+
} else {
72+
dst.push(path);
73+
}
74+
}
75+
76+
Ok(())
77+
}
78+
79+
/// Validate the actual and expected values.
80+
pub fn df_value_validator(
81+
normalizer: Normalizer,
82+
actual: &[Vec<String>],
83+
expected: &[String],
84+
) -> bool {
85+
let normalized_expected = expected.iter().map(normalizer).collect::<Vec<_>>();
86+
let normalized_actual = actual
87+
.iter()
88+
.map(|strs| strs.iter().join(" "))
89+
.map(|str| str.trim_end().to_string())
90+
.collect_vec();
91+
92+
if log_enabled!(Warn) && normalized_actual != normalized_expected {
93+
warn!("df validation failed. actual vs expected:");
94+
for i in 0..normalized_actual.len() {
95+
warn!("[{i}] {}<eol>", normalized_actual[i]);
96+
warn!(
97+
"[{i}] {}<eol>",
98+
if normalized_expected.len() >= i {
99+
&normalized_expected[i]
100+
} else {
101+
"No more results"
102+
}
103+
);
104+
}
105+
}
106+
107+
normalized_actual == normalized_expected
108+
}

0 commit comments

Comments
 (0)