Add UPDATE_EXPECT envvar to update tests (#748)

This commit is contained in:
Alex Saveau 2023-04-20 01:47:31 -07:00 committed by GitHub
parent f16ac4d258
commit c505a0f5dc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 617 additions and 151 deletions

669
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -12,6 +12,7 @@ comemo = "0.2.2"
elsa = "1.7" elsa = "1.7"
iai = { git = "https://github.com/reknih/iai" } iai = { git = "https://github.com/reknih/iai" }
once_cell = "1" once_cell = "1"
oxipng = "8.0.0"
tiny-skia = "0.6.2" tiny-skia = "0.6.2"
ttf-parser = "0.17" ttf-parser = "0.17"
unscanny = "0.1" unscanny = "0.1"

View File

@ -50,18 +50,19 @@ making changes.
testit --pdf testit --pdf
``` ```
## Creating new tests ## Update expected images
To keep things small, please optimize reference images before committing them. If you created a new test or fixed a bug in an existing test, you need to update
When you use the approve button from the Test Helper (see the `tools` folder) the reference image used for comparison. For this, you can use the
this happens automatically if you have `oxipng` installed. `UPDATE_EXPECT` environment varariable or the `--update` flag:
```bash ```bash
# One image testit mytest --update
oxipng -o max path/to/image.png
# All images
oxipng -r -o max tests/ref
``` ```
If you use the VS Code test helper extension (see the `tools` folder), you can
alternatively use the checkmark button to update the reference image. In that
case you should also install `oxipng` on your system so that the test helper
can optimize the reference images.
## Making an alias ## Making an alias
If you want to have a quicker way to run the tests, consider adding a shortcut If you want to have a quicker way to run the tests, consider adding a shortcut
to your shell profile so that you can simply write something like: to your shell profile so that you can simply write something like:

View File

@ -11,6 +11,7 @@ use std::path::{Path, PathBuf};
use comemo::{Prehashed, Track}; use comemo::{Prehashed, Track};
use elsa::FrozenVec; use elsa::FrozenVec;
use once_cell::unsync::OnceCell; use once_cell::unsync::OnceCell;
use oxipng::{InFile, Options, OutFile};
use tiny_skia as sk; use tiny_skia as sk;
use typst::diag::{bail, FileError, FileResult}; use typst::diag::{bail, FileError, FileResult};
use typst::doc::{Document, Frame, FrameItem, Meta}; use typst::doc::{Document, Frame, FrameItem, Meta};
@ -77,14 +78,27 @@ fn main() {
let pdf_path = let pdf_path =
args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf")); args.pdf.then(|| Path::new(PDF_DIR).join(path).with_extension("pdf"));
ok += test(&mut world, &src_path, &png_path, &ref_path, pdf_path.as_deref()) ok += test(
as usize; &mut world,
&src_path,
&png_path,
&ref_path,
pdf_path.as_deref(),
args.update,
) as usize;
} }
if len > 1 { if len > 1 {
println!("{ok} / {len} tests passed."); println!("{ok} / {len} tests passed.");
} }
if ok != len {
println!(
"Set the UPDATE_EXPECT environment variable or pass the \
--update flag to update the reference image(s)."
);
}
if ok < len { if ok < len {
std::process::exit(1); std::process::exit(1);
} }
@ -95,6 +109,7 @@ struct Args {
filter: Vec<String>, filter: Vec<String>,
exact: bool, exact: bool,
pdf: bool, pdf: bool,
update: bool,
print: PrintConfig, print: PrintConfig,
} }
@ -111,6 +126,7 @@ impl Args {
let mut filter = Vec::new(); let mut filter = Vec::new();
let mut exact = false; let mut exact = false;
let mut pdf = false; let mut pdf = false;
let mut update = env::var_os("UPDATE_EXPECT").is_some();
let mut print = PrintConfig::default(); let mut print = PrintConfig::default();
for arg in args { for arg in args {
@ -121,6 +137,8 @@ impl Args {
"--exact" => exact = true, "--exact" => exact = true,
// Generate PDFs. // Generate PDFs.
"--pdf" => pdf = true, "--pdf" => pdf = true,
// Update the reference images.
"--update" => update = true,
// Debug print the syntax trees. // Debug print the syntax trees.
"--syntax" => print.syntax = true, "--syntax" => print.syntax = true,
// Debug print the model. // Debug print the model.
@ -132,7 +150,7 @@ impl Args {
} }
} }
Self { filter, exact, pdf, print } Self { filter, exact, pdf, update, print }
} }
fn matches(&self, path: &Path) -> bool { fn matches(&self, path: &Path) -> bool {
@ -339,6 +357,7 @@ fn test(
png_path: &Path, png_path: &Path,
ref_path: &Path, ref_path: &Path,
pdf_path: Option<&Path>, pdf_path: Option<&Path>,
update: bool,
) -> bool { ) -> bool {
let name = src_path.strip_prefix(TYP_DIR).unwrap_or(src_path); let name = src_path.strip_prefix(TYP_DIR).unwrap_or(src_path);
println!("Testing {}", name.display()); println!("Testing {}", name.display());
@ -346,6 +365,7 @@ fn test(
let text = fs::read_to_string(src_path).unwrap(); let text = fs::read_to_string(src_path).unwrap();
let mut ok = true; let mut ok = true;
let mut updated = false;
let mut frames = vec![]; let mut frames = vec![];
let mut line = 0; let mut line = 0;
let mut compare_ref = true; let mut compare_ref = true;
@ -404,16 +424,26 @@ fn test(
.zip(ref_pixmap.data()) .zip(ref_pixmap.data())
.any(|(&a, &b)| a.abs_diff(b) > 2) .any(|(&a, &b)| a.abs_diff(b) > 2)
{ {
println!(" Does not match reference image. ❌"); if update {
ok = false; update_image(png_path, ref_path);
updated = true;
} else {
println!(" Does not match reference image. ❌");
ok = false;
}
} }
} else if !document.pages.is_empty() { } else if !document.pages.is_empty() {
println!(" Failed to open reference image. ❌"); if update {
ok = false; update_image(png_path, ref_path);
updated = true;
} else {
println!(" Failed to open reference image. ❌");
ok = false;
}
} }
} }
if ok { if ok && !updated {
if world.print == PrintConfig::default() { if world.print == PrintConfig::default() {
print!("\x1b[1A"); print!("\x1b[1A");
} }
@ -423,6 +453,16 @@ fn test(
ok ok
} }
fn update_image(png_path: &Path, ref_path: &Path) {
println!(" Updated reference image. ✔");
oxipng::optimize(
&InFile::Path(png_path.to_owned()),
&OutFile::Path(Some(ref_path.to_owned())),
&Options::max_compression(),
)
.unwrap();
}
fn test_part( fn test_part(
world: &mut TestWorld, world: &mut TestWorld,
src_path: &Path, src_path: &Path,

View File

@ -7,5 +7,8 @@ the `tests` folder.
- Open: Opens the output and reference images of a test to the side. - Open: Opens the output and reference images of a test to the side.
- Refresh: Refreshes the preview. - Refresh: Refreshes the preview.
- Rerun: Re-runs the test. - Rerun: Re-runs the test.
- Approve: Copies the output into the reference folder and optimizes - Update: Copies the output into the reference folder and optimizes
it with `oxipng`. it with `oxipng`.
For the test helper to work correctly, you also need to install `oxipng`, for
example with `cargo install oxipng`.

View File

@ -51,7 +51,7 @@ function activate(context) {
) )
}) })
const approveCmd = vscode.commands.registerCommand("ShortcutMenuBar.testApprove", () => { const updateCmd = vscode.commands.registerCommand("ShortcutMenuBar.testUpdate", () => {
const uri = vscode.window.activeTextEditor.document.uri const uri = vscode.window.activeTextEditor.document.uri
const { pngPath, refPath } = getPaths(uri) const { pngPath, refPath } = getPaths(uri)
@ -66,7 +66,7 @@ function activate(context) {
context.subscriptions.push(openCmd) context.subscriptions.push(openCmd)
context.subscriptions.push(refreshCmd) context.subscriptions.push(refreshCmd)
context.subscriptions.push(rerunCmd) context.subscriptions.push(rerunCmd)
context.subscriptions.push(approveCmd) context.subscriptions.push(updateCmd)
} }
function getPaths(uri) { function getPaths(uri) {

View File

Before

Width:  |  Height:  |  Size: 539 B

After

Width:  |  Height:  |  Size: 539 B

View File

Before

Width:  |  Height:  |  Size: 539 B

After

Width:  |  Height:  |  Size: 539 B

View File

@ -1,7 +1,7 @@
{ {
"name": "typst-test-helper", "name": "typst-test-helper",
"displayName": "Typst Test Helper", "displayName": "Typst Test Helper",
"description": "Helps to run, compare and approve Typst tests.", "description": "Helps to run, compare and update Typst tests.",
"version": "0.0.1", "version": "0.0.1",
"engines": { "engines": {
"vscode": "^1.53.0" "vscode": "^1.53.0"
@ -13,7 +13,7 @@
"onCommand:ShortcutMenuBar.testOpen", "onCommand:ShortcutMenuBar.testOpen",
"onCommand:ShortcutMenuBar.testRefresh", "onCommand:ShortcutMenuBar.testRefresh",
"onCommand:ShortcutMenuBar.testRerun", "onCommand:ShortcutMenuBar.testRerun",
"onCommand:ShortcutMenuBar.testApprove" "onCommand:ShortcutMenuBar.testUpdate"
], ],
"main": "./extension.js", "main": "./extension.js",
"contributes": { "contributes": {
@ -46,12 +46,12 @@
} }
}, },
{ {
"command": "ShortcutMenuBar.testApprove", "command": "ShortcutMenuBar.testUpdate",
"title": "Approve output", "title": "Update reference image",
"category": "ShortcutMenuBar", "category": "ShortcutMenuBar",
"icon": { "icon": {
"light": "images/approve-light.svg", "light": "images/update-light.svg",
"dark": "images/approve-dark.svg" "dark": "images/update-dark.svg"
} }
} }
], ],
@ -74,7 +74,7 @@
}, },
{ {
"when": "resourceExtname == .typ && resourcePath =~ /.*tests.*/", "when": "resourceExtname == .typ && resourcePath =~ /.*tests.*/",
"command": "ShortcutMenuBar.testApprove", "command": "ShortcutMenuBar.testUpdate",
"group": "navigation@3" "group": "navigation@3"
} }
] ]