Skip to content

feat(runner): support <slt:ignore> #266

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
May 26, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Unreleased

* runner: Add `<slt:ignore>` to skip the volatile parts of the output.

## [0.28.3] - 2025-05-16

* bin: Add `--shutdown-timeout` to set a timeout for shutting down the database connections after a test file is finished. By default, this is unspecified, meaning to wait forever.
Expand Down
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,19 @@ SELECT * FROM foo;
4 5
```

### Extension: Ignore volatile parts of output

You can use `<slt:ignore>` to skip the volatile parts of the output. This is helpful for e.g., testing the format
of `EXPLAIN`-like statements.

```text
query T
EXPLAIN SELECT * FROM foo;
----
Seq Scan on t (cost=<slt:ignore> rows=<slt:ignore> width=<slt:ignore>)
Filter: (x > 1)
```

### Extension: Run a query/statement that should fail with the expacted error message

The syntax:
Expand Down
70 changes: 70 additions & 0 deletions sqllogictest/src/runner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -487,6 +487,44 @@ pub fn default_validator(
actual: &[Vec<String>],
expected: &[String],
) -> bool {
// Support ignore marker <slt:ignore> to skip volatile parts of output.
const IGNORE_MARKER: &str = "<slt:ignore>";
let contains_ignore_marker = expected.iter().any(|line| line.contains(IGNORE_MARKER));

// Normalize expected lines.
// If ignore marker present, perform fragment-based matching on the full snapshot.
if contains_ignore_marker {
// If ignore marker present, perform fragment-based matching on the full snapshot.
// The actual results might contain \n, and may not be a normal "row", which is not suitable to normalize.
let expected_results = expected;
let actual_rows = actual
.iter()
.map(|strs| strs.iter().join(" "))
.collect_vec();

let expected_snapshot = expected_results.join("\n");
let actual_snapshot = actual_rows.join("\n");
let fragments: Vec<&str> = expected_snapshot.split(IGNORE_MARKER).collect();
let mut pos = 0;
for frag in fragments {
if frag.is_empty() {
continue;
}
if let Some(idx) = actual_snapshot[pos..].find(frag) {
pos += idx + frag.len();
} else {
tracing::error!(
"mismatch at: {}\nexpected: {}\nactual: {}",
pos,
frag,
&actual_snapshot[pos..]
);
return false;
}
}
return true;
}

let expected_results = expected.iter().map(normalizer).collect_vec();
// Default, we compare normalized results. Whitespace characters are ignored.
let normalized_rows = actual
Expand Down Expand Up @@ -2294,4 +2332,36 @@ Caused by:
write!(f, "TestError: {}", self.0)
}
}

#[test]
fn test_default_validator_ignore_simple() {
let normalizer = default_normalizer;
let actual = vec![vec!["foo".to_string(), "bar".to_string()]];
let expected = vec!["foo<slt:ignore>bar".to_string()];
assert!(default_validator(normalizer, &actual, &expected));
}

#[test]
fn test_default_validator_ignore_multiple_fragments() {
let normalizer = default_normalizer;
let actual = vec![vec![
"one".to_string(),
"two".to_string(),
"three".to_string(),
]];
let expected = vec!["one<slt:ignore>three".to_string()];
assert!(default_validator(normalizer, &actual, &expected));
}

#[test]
fn test_default_validator_ignore_fail() {
let normalizer = default_normalizer;
let actual = vec![vec![
"alpha".to_string(),
"beta".to_string(),
"gamma".to_string(),
]];
let expected = vec!["alpha<slt:ignore>delta".to_string()];
assert!(!default_validator(normalizer, &actual, &expected));
}
}
Loading