Skip to content

Commit

Permalink
Revert "test: Refactor book snapshot tests (#1900)"
Browse files Browse the repository at this point in the history
This reverts commit abc61ff.
  • Loading branch information
max-sixty authored Feb 20, 2023
1 parent abc61ff commit aa6a09c
Show file tree
Hide file tree
Showing 194 changed files with 1,103 additions and 1,010 deletions.
172 changes: 73 additions & 99 deletions book/tests/snapshot.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![cfg(not(target_family = "wasm"))]
/// This test:
/// - Extracts PRQL code blocks into files in the `examples` path
/// - Extracts PRQL code blocks into the `examples` path.
/// - Converts them to SQL using insta, raising an error if there's a diff.
/// - Replaces the PRQL code block with a comparison table.
///
Expand All @@ -18,38 +18,69 @@
// us. They introduce a bunch of non-rust dependencies, which is not ideal, but
// passable. They don't let us customize our formatting (e.g. in a table).
//
use anyhow::{bail, Error, Result};
use anyhow::{bail, Result};
use globset::Glob;
use insta::{assert_snapshot, glob};
use itertools::Itertools;
use insta::{assert_display_snapshot, assert_snapshot, glob};
use log::warn;
use prql_compiler::*;
use pulldown_cmark::{CodeBlockKind, Event, Parser, Tag};
use std::fs;
use std::path::Path;
use std::{collections::HashMap, fs};
use walkdir::WalkDir;

#[test]
fn test_examples() -> Result<()> {
fn run_examples() -> Result<()> {
// TODO: In CI this could pass by replacing incorrect files. To catch that,
// we could check if there are any diffs after this has run?

// Note that on windows, markdown is read differently, and so
// writing on Windows. ref https://github.com/PRQL/prql/issues/356
#[cfg(not(target_family = "windows"))]
write_prql_examples()?;
test_prql_examples();
write_reference_prql()?;
run_reference_prql();

// TODO: Currently we run this in the same test, since we need the
// `write_reference_prql` function to have been run. If we could iterate
// over the PRQL examples without writing them to disk, we could run this as
// a separate test. (Though then we'd lose the deferred failures feature
// that insta's `glob!` macro provides.)
run_display_reference_prql();

Ok(())
}

const ROOT_EXAMPLES_PATH: &str = "tests/prql";
/// Extract reference examples from the PRQL docs and write them to the
/// `tests/prql` path, one in each file.
// We could alternatively have used something like
// https://github.com/earldouglas/codedown, but it's not much code, and it
// requires no dependencies.
//
// We allow dead_code because of the window issue described above. (Can we allow
// it only for windows?)
#[allow(dead_code)]
fn write_reference_prql() -> Result<()> {
// Remove old .prql files, since we're going to rewrite them, and we don't want
// old files which wouldn't be rewritten from hanging around.
// We use `trash`, since we don't want to be removing files with test code
// in case there's a bug.
//
// A more elegant approach would be to keep a list of the files and remove
// the ones we don't write.

let examples_path = Path::new("tests/prql");
if examples_path.exists() {
trash::delete(Path::new("tests/prql")).unwrap_or_else(|e| {
warn!("Failed to delete old examples: {}", e);
});
}

/// Collect all the PRQL examples in the book, as a map of <Path, PRQL>.
fn collect_book_examples() -> Result<HashMap<String, String>> {
let glob = Glob::new("**/*.md")?.compile_matcher();
let examples_in_book: HashMap<String, String> = WalkDir::new(Path::new("./src/"))

WalkDir::new(Path::new("./src/"))
.into_iter()
.flatten()
.filter(|x| glob.is_match(x.path()))
.flat_map(|dir_entry| {
.try_for_each(|dir_entry| {
let text = fs::read_to_string(dir_entry.path())?;
let mut parser = Parser::new(&text);
let mut prql_blocks = vec![];
Expand All @@ -71,85 +102,33 @@ fn collect_book_examples() -> Result<HashMap<String, String>> {
_ => {}
}
}
let snapshot_prefix = &dir_entry
.path()
.strip_prefix("./src/")?
.to_str()
.unwrap()
.trim_end_matches(".md");
Ok(prql_blocks

// Write each one to a new file.
prql_blocks
.iter()
.enumerate()
.map(|(i, example)| {
(
format!("{ROOT_EXAMPLES_PATH}/{snapshot_prefix}-{i}.prql"),
example.to_string(),
)
})
.collect::<HashMap<_, _>>())
})
.flatten()
.collect();

Ok(examples_in_book)
}

/// Extract reference examples from the PRQL docs and write them to the
/// `tests/prql` path, one in each file.
// We could alternatively have used something like
// https://github.com/earldouglas/codedown, but it's not much code, and it
// requires no dependencies.
//
// We allow dead_code because of the window issue described above. (Can we allow
// it only for windows?)
#[allow(dead_code)]
fn write_prql_examples() -> Result<()> {
// If we have to modify any files, raise an error at the end, so it fails in CI.
let mut snapshots_updated = false;

let examples_path = Path::new(ROOT_EXAMPLES_PATH);
let prql_matcher = Glob::new("**.prql")?.compile_matcher();
let mut existing_snapshots: HashMap<_, _> = WalkDir::new(examples_path)
.into_iter()
.flatten()
.filter(|x| prql_matcher.is_match(x.path()))
.map(|x| x.path().to_owned())
.map(|x| Ok::<_, Error>((x.to_string_lossy().to_string(), fs::read_to_string(x)?)))
.try_collect()?;

// Write any new snapshots, or update any that have changed. =
collect_book_examples()?
.iter()
.try_for_each(|(prql_path, example)| {
if existing_snapshots
.remove(prql_path)
.map(|existing| existing != *example)
.unwrap_or(true)
{
snapshots_updated = true;
fs::create_dir_all(Path::new(prql_path).parent().unwrap())?;
fs::write(prql_path, example)?;
}

Ok::<(), anyhow::Error>(())
.try_for_each(|(i, example)| {
let file_relative = &dir_entry
.path()
.strip_prefix("./src/")?
.to_str()
.unwrap()
.trim_end_matches(".md");
let prql_path = format!("tests/prql/{file_relative}-{i}.prql");

fs::create_dir_all(Path::new(&prql_path).parent().unwrap())?;
fs::write(prql_path, example.to_string())?;

Ok::<(), anyhow::Error>(())
})?;
Ok(())
})?;

// If there are any files left in `existing_snapshots`, we remove them, since
// they don't reference anything.
existing_snapshots.iter().for_each(|(path, _)| {
trash::delete(path).unwrap_or_else(|e| {
warn!("Failed to delete unreferenced example: {}", e);
})
});

if snapshots_updated {
bail!("Some book snapshots were not consistent with the queries in the book and have been updated. Subsequent runs should pass.");
}
Ok(())
}

/// Snapshot the output of each example.
fn test_prql_examples() {
fn run_reference_prql() {
glob!("prql/**/*.prql", |path| {
let prql = fs::read_to_string(path).unwrap();

Expand All @@ -168,25 +147,20 @@ fn test_prql_examples() {
}

/// Snapshot the display trait output of each example.
// Currently not a separate test, see notes in caller.
//
// TODO: this involves writing out almost the same PRQL again — instead we could
// compare the output of Display to the auto-formatted source. But we need an
// autoformatter for that (unless we want to raise on any non-matching input,
// which seems very strict)
#[test]
fn test_display() -> Result<(), ErrorMessages> {
use prql_compiler::downcast;
collect_book_examples()
.map_err(downcast)?
.iter()
.try_for_each(|(path, example)| {
assert_snapshot!(
path.to_owned(),
prql_to_pl(example).and_then(pl_to_prql)?,
example
);
Ok::<(), ErrorMessages>(())
})?;
fn run_display_reference_prql() {
glob!("prql/**/*.prql", |path| {
let prql = fs::read_to_string(path).unwrap();

Ok(())
if prql.contains("skip_test") {
return;
}

assert_display_snapshot!(prql_to_pl(&prql).and_then(pl_to_prql).unwrap());
});
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "let newest_employees = (\n from employees\n sort tenure\n take 50\n)\n\nlet average_salaries = (\n from salaries\n group country (\n aggregate average_country_salary = (average salary)\n )\n)\n\nfrom newest_employees\njoin average_salaries [==country]\nselect [name, salary, average_country_salary]\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/cte-0.prql
---
let newest_employees = (
from employees
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "from salaries\ngroup [emp_no] (\n aggregate [emp_salary = average salary]\n)\njoin t=titles [==emp_no]\njoin dept_emp side:left [==emp_no]\ngroup [dept_emp.dept_no, t.title] (\n aggregate [avg_salary = average emp_salary]\n)\njoin departments [==dept_no]\nselect [dept_name, title, avg_salary]\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/employees-0.prql
---
from salaries
group [emp_no] (
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "from e=employees\njoin salaries [==emp_no]\ngroup [e.emp_no, e.gender] (\n aggregate [\n emp_salary = average salaries.salary\n ]\n)\njoin de=dept_emp [==emp_no] side:left\ngroup [de.dept_no, gender] (\n aggregate [\n salary_avg = average emp_salary,\n salary_sd = stddev emp_salary,\n ]\n)\njoin departments [==dept_no]\nselect [dept_name, gender, salary_avg, salary_sd]\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/employees-1.prql
---
from e = employees
join salaries [==emp_no]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "from e=employees\njoin salaries [==emp_no]\ngroup [e.emp_no, e.gender] (\n aggregate [\n emp_salary = average salaries.salary\n ]\n)\njoin de=dept_emp [==emp_no]\njoin dm=dept_manager [\n (dm.dept_no == de.dept_no) and s\"(de.from_date, de.to_date) OVERLAPS (dm.from_date, dm.to_date)\"\n]\ngroup [dm.emp_no, gender] (\n aggregate [\n salary_avg = average emp_salary,\n salary_sd = stddev emp_salary\n ]\n)\nderive mng_no = emp_no\njoin managers=employees [==emp_no]\nderive mng_name = s\"managers.first_name || ' ' || managers.last_name\"\nselect [mng_name, managers.gender, salary_avg, salary_sd]\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/employees-2.prql
---
from e = employees
join salaries [==emp_no]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "from de=dept_emp\njoin s=salaries side:left [\n (s.emp_no == de.emp_no),\n s\"({s.from_date}, {s.to_date}) OVERLAPS ({de.from_date}, {de.to_date})\"\n]\ngroup [de.emp_no, de.dept_no] (\n aggregate salary = (average s.salary)\n)\njoin employees [==emp_no]\njoin titles [==emp_no]\nselect [dept_no, salary, employees.gender, titles.title]\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/employees-3.prql
---
from de = dept_emp
join side:left s = salaries [
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: book/tests/snapshot.rs
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/list-equivalence-0.prql
---
from employees
select salary



Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
source: book/tests/snapshot.rs
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/list-equivalence-1.prql
---
from employees
select [salary]



Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "from employees\nderive [\n gross_salary = salary + payroll_tax,\n gross_cost = gross_salary + benefits_cost\n]\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/list-equivalence-2.prql
---
from employees
derive [
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
---
source: book/tests/snapshot.rs
expression: "from employees\nderive gross_salary = salary + payroll_tax\nderive gross_cost = gross_salary + benefits_cost\n"
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/list-equivalence-3.prql
---
from employees
derive gross_salary = salary + payroll_tax
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
source: book/tests/snapshot.rs
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/misc-0.prql
---
let parts = (
from seq_1_to_5
)



from pl = prospect_lists_prospects
filter prospect_list_id == "cc675eee-8bd1-237f-be5e-622ba511d65e"
join a = accounts [a.id == pl.related_id]
join er = email_addr_bean_rel [er.bean_id == a.id and er.primary_address == "1"]
join ea = email_addresses [ea.id == er.email_address_id]
select ea.email_address
derive prefix = s"regexp_replace(SUBSTRING_INDEX({email_address}, '@', 1), '[.0-9-_:]+', '.')"
derive stub = s"SUBSTRING_INDEX(SUBSTRING_INDEX({prefix}, '.', part), '.', -1)"
select [
email_address,
stub,
]



Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
source: book/tests/snapshot.rs
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/misc-1.prql
---
from club_ratings
filter rating != null
group year (
derive [rating_norm = rating - ( average rating ) / ( stddev rating )]
)



Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
---
source: book/tests/snapshot.rs
expression: prql_to_pl(&prql).and_then(pl_to_prql).unwrap()
input_file: book/tests/prql/examples/variables-0.prql
---
from employees
filter country == "USA"
derive [
gross_salary = salary + payroll_tax,
gross_cost = gross_salary + benefits_cost,
]
filter gross_cost > 0
group [
title,
country,
] (
aggregate [
average salary,
average gross_salary,
sum salary,
sum gross_salary,
average gross_cost,
sum_gross_cost = sum gross_cost,
ct = count,
]
)
sort sum_gross_cost
filter ct > 200
take 20



Loading

0 comments on commit aa6a09c

Please sign in to comment.