diff --git a/.gitignore b/.gitignore index 32eee3e97..f60c09f7d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,8 +1,15 @@ .vscode +_things + /target +bench/target **/*.rs.bk Cargo.lock -bench/target -tests/out -_things + +tests/png +tests/pdf +tests/playground.typ +tests/playground.png +tests/playground.pdf + tarpaulin-report.html diff --git a/tests/README.md b/tests/README.md index 424c97d8d..fef134820 100644 --- a/tests/README.md +++ b/tests/README.md @@ -6,5 +6,5 @@ `oxipng -o max tests/ref/` when creating or updating reference images (note that `` can be `*` to optimize all images). - `res`: Resource files used by tests. -- `out`: PNG and PDF files produced by tests. - +- `png`: PNG files produced by tests. +- `pdf`: PDF files produced by tests. diff --git a/tests/ref/func-font-fallback.png b/tests/ref/func-font-fallback.png deleted file mode 100644 index b7c565b41..000000000 Binary files a/tests/ref/func-font-fallback.png and /dev/null differ diff --git a/tests/ref/func-font-properties.png b/tests/ref/func-font-properties.png deleted file mode 100644 index 3dab43f58..000000000 Binary files a/tests/ref/func-font-properties.png and /dev/null differ diff --git a/tests/ref/func-font.png b/tests/ref/func-font.png new file mode 100644 index 000000000..188414903 Binary files /dev/null and b/tests/ref/func-font.png differ diff --git a/tests/ref/func-h-and-v.png b/tests/ref/func-h-and-v.png index e977046f3..6f47ae09b 100644 Binary files a/tests/ref/func-h-and-v.png and b/tests/ref/func-h-and-v.png differ diff --git a/tests/ref/func-image-fit.png b/tests/ref/func-image-fit.png deleted file mode 100644 index b89e78fd1..000000000 Binary files a/tests/ref/func-image-fit.png and /dev/null differ diff --git a/tests/ref/func-image-formats.png b/tests/ref/func-image-formats.png deleted file mode 100644 index 45b23276b..000000000 Binary files a/tests/ref/func-image-formats.png and /dev/null differ diff --git a/tests/ref/func-image.png b/tests/ref/func-image.png new file mode 100644 index 000000000..e2b647bfe Binary files /dev/null and b/tests/ref/func-image.png differ diff --git a/tests/ref/func-page-body.png b/tests/ref/func-page-body.png deleted file mode 100644 index 09f685c33..000000000 Binary files a/tests/ref/func-page-body.png and /dev/null differ diff --git a/tests/ref/func-page-dirs.png b/tests/ref/func-page-dirs.png deleted file mode 100644 index 2bf23ebcc..000000000 Binary files a/tests/ref/func-page-dirs.png and /dev/null differ diff --git a/tests/ref/func-page-metrics.png b/tests/ref/func-page-metrics.png deleted file mode 100644 index 2e8d626db..000000000 Binary files a/tests/ref/func-page-metrics.png and /dev/null differ diff --git a/tests/ref/func-page.png b/tests/ref/func-page.png new file mode 100644 index 000000000..9bde2dae6 Binary files /dev/null and b/tests/ref/func-page.png differ diff --git a/tests/typ/func-font-error.typ b/tests/typ/func-font-error.typ deleted file mode 100644 index b75a4fb7f..000000000 --- a/tests/typ/func-font-error.typ +++ /dev/null @@ -1,21 +0,0 @@ -// Test error cases of the `font` function. - -// Not one of the valid things for positional arguments. -[font false] - -// Wrong types. -[font style: bold, weight: "thin", serif: 0] - -// Weight out of range. -[font weight: 2700] - -// Non-existing argument. -[font something: "invalid"] - -// compare-ref: false -// error: 4:7-4:12 unexpected argument -// error: 7:14-7:18 expected font style, found font weight -// error: 7:28-7:34 expected font weight, found string -// error: 7:43-7:44 expected font family or array of font families, found integer -// warning: 10:15-10:19 must be between 100 and 900 -// error: 13:7-13:27 unexpected argument diff --git a/tests/typ/func-font-fallback.typ b/tests/typ/func-font-fallback.typ deleted file mode 100644 index c6dd81f05..000000000 --- a/tests/typ/func-font-fallback.typ +++ /dev/null @@ -1,20 +0,0 @@ -// Test font fallback. - -// Source Sans Pro + Segoe UI Emoji. -Emoji: 🏀 - -// CMU Serif + Noto Emoji. -[font "CMU Serif", "Noto Emoji"][ - Emoji: 🏀 -] - -// Class definitions. -[font serif: ("CMU Serif", "Latin Modern Math", "Noto Emoji")] -[font serif][ - Math: ∫ α + β ➗ 3 -] - -// Class definition reused. -[font sans-serif: "Noto Emoji"] -[font sans-serif: ("Archivo", sans-serif)] -New sans-serif. 🚀 diff --git a/tests/typ/func-font-properties.typ b/tests/typ/func-font-properties.typ deleted file mode 100644 index af8b88914..000000000 --- a/tests/typ/func-font-properties.typ +++ /dev/null @@ -1,20 +0,0 @@ -// Test configuring font properties. - -[font "PT Sans", 10pt] - -// Set same font size in three different ways. -[font 20pt][A] -[font 200%][A] -[font 15pt + 50%][A] - -// Do nothing. -[font][Normal] - -// Set style (is available). -[font style: italic][Italic] - -// Set weight (is available). -[font weight: bold][Bold] - -// Set stretch (not available, matching closest). -[font stretch: ultra-condensed][Condensed] diff --git a/tests/typ/func-font.typ b/tests/typ/func-font.typ new file mode 100644 index 000000000..c73e3a1a6 --- /dev/null +++ b/tests/typ/func-font.typ @@ -0,0 +1,65 @@ +// Test configuring font properties. + +[font "PT Sans", 10pt] + +// Set same font size in three different ways. +[font 20pt][A] +[font 200%][A] +[font 15pt + 50%][A] + +// Do nothing. +[font][Normal] + +// Set style (is available). +[font style: italic][Italic] + +// Set weight (is available). +[font weight: bold][Bold] + +// Set stretch (not available, matching closest). +[font stretch: ultra-condensed][Condensed] + +--- +// Test font fallback. + +// Source Sans Pro + Segoe UI Emoji. +Emoji: 🏀 + +// CMU Serif + Noto Emoji. +[font "CMU Serif", "Noto Emoji"][ + Emoji: 🏀 +] + +// Class definitions. +[font serif: ("CMU Serif", "Latin Modern Math", "Noto Emoji")] +[font serif][ + Math: ∫ α + β ➗ 3 +] + +// Class definition reused. +[font sans-serif: "Noto Emoji"] +[font sans-serif: ("Archivo", sans-serif)] +New sans-serif. 🚀 + +--- +// Test error cases. +// +// ref: false +// error: 3:7-3:12 unexpected argument +// error: 6:14-6:18 expected font style, found font weight +// error: 6:28-6:34 expected font weight, found string +// error: 6:43-6:44 expected font family or array of font families, found integer +// warning: 9:15-9:19 must be between 100 and 900 +// error: 12:7-12:27 unexpected argument + +// Not one of the valid things for positional arguments. +[font false] + +// Wrong types. +[font style: bold, weight: "thin", serif: 0] + +// Weight out of range. +[font weight: 2700] + +// Non-existing argument. +[font something: "invalid"] diff --git a/tests/typ/func-h-and-v.typ b/tests/typ/func-h-and-v.typ index 0587b3eda..7b8843c83 100644 --- a/tests/typ/func-h-and-v.typ +++ b/tests/typ/func-h-and-v.typ @@ -12,9 +12,6 @@ Add [h 10pt] [h 10pt] up // Relative to font size. Relative [h 100%] spacing -// Missing spacing. -Totally [h] ignored - // Swapped axes. [page main-dir: rtl, cross-dir: ttb][ 1 [h 1cm] 2 @@ -22,4 +19,10 @@ Totally [h] ignored 3 [v 1cm] 4 [v -1cm] 5 ] -// error: 16:11-16:11 missing argument: spacing +--- +// Test error cases. +// +// error: 3:11-3:11 missing argument: spacing + +// Missing spacing. +Totally [h] ignored diff --git a/tests/typ/func-image-error.typ b/tests/typ/func-image-error.typ deleted file mode 100644 index 155534200..000000000 --- a/tests/typ/func-image-error.typ +++ /dev/null @@ -1,11 +0,0 @@ -// Test error cases of the `image` function. - -// File does not exist. -[image "path/does/not/exist"] - -// File exists, but is no image. -[image "typ/image-error.typ"] - -// compare-ref: false -// error: 4:8-4:29 failed to load image -// error: 7:8-7:29 failed to load image diff --git a/tests/typ/func-image-formats.typ b/tests/typ/func-image-formats.typ deleted file mode 100644 index c12e36398..000000000 --- a/tests/typ/func-image-formats.typ +++ /dev/null @@ -1,8 +0,0 @@ -// Test loading different image formats. - -// Load an RGBA PNG image. -[image "res/rhino.png"] -[pagebreak] - -// Load an RGB JPEG image. -[image "res/tiger.jpg"] diff --git a/tests/typ/func-image-fit.typ b/tests/typ/func-image.typ similarity index 57% rename from tests/typ/func-image-fit.typ rename to tests/typ/func-image.typ index a9855aa4f..78a644c04 100644 --- a/tests/typ/func-image-fit.typ +++ b/tests/typ/func-image.typ @@ -1,3 +1,13 @@ +// Test loading different image formats. + +// Load an RGBA PNG image. +[image "res/rhino.png"] +[pagebreak] + +// Load an RGB JPEG image. +[image "res/tiger.jpg"] + +--- // Test configuring the size and fitting behaviour of images. // Fit to width of page. @@ -21,3 +31,16 @@ [align bottom, right][ [image "res/tiger.jpg"] ] + +--- +// Test error cases. +// +// ref: false +// error: 3:8-3:29 failed to load image +// error: 6:8-6:29 failed to load image + +// File does not exist. +[image "path/does/not/exist"] + +// File exists, but is no image. +[image "typ/image-error.typ"] diff --git a/tests/typ/func-page-body.typ b/tests/typ/func-page-body.typ deleted file mode 100644 index bfa9775d2..000000000 --- a/tests/typ/func-page-body.typ +++ /dev/null @@ -1,11 +0,0 @@ -// Test a combination of pages with bodies and normal content. - -[page height: 50pt] - -[page][First] -[page][Second] -[pagebreak] -Fourth -[page][] -Sixth -[page][Seventh and last] diff --git a/tests/typ/func-page-dirs.typ b/tests/typ/func-page-dirs.typ deleted file mode 100644 index 47e8ae157..000000000 --- a/tests/typ/func-page-dirs.typ +++ /dev/null @@ -1,5 +0,0 @@ -// Test changing the layouting directions of pages. - -[page main-dir: btt, cross-dir: rtl] - -Right to left! diff --git a/tests/typ/func-page-error.typ b/tests/typ/func-page-error.typ deleted file mode 100644 index 21370fa8b..000000000 --- a/tests/typ/func-page-error.typ +++ /dev/null @@ -1,11 +0,0 @@ -// Test error cases of the `page` function. - -// Invalid paper. -[page nonexistant] - -// Aligned axes. -[page main-dir: ltr] - -// compare-ref: false -// error: 4:7-4:18 unknown variable -// error: 7:17-7:20 aligned axis diff --git a/tests/typ/func-page-metrics.typ b/tests/typ/func-page.typ similarity index 54% rename from tests/typ/func-page-metrics.typ rename to tests/typ/func-page.typ index 3b54d13f8..7f87a414d 100644 --- a/tests/typ/func-page-metrics.typ +++ b/tests/typ/func-page.typ @@ -23,3 +23,37 @@ // Flip the page. [page "a10", flip: true][Flipped] + +--- +// Test a combination of pages with bodies and normal content. + +[page height: 50pt] + +[page][First] +[page][Second] +[pagebreak] +Fourth +[page][] +Sixth +[page][Seventh and last] + + +--- +// Test changing the layouting directions of pages. + +[page main-dir: btt, cross-dir: rtl] + +Right to left! + +--- +// Test error cases. +// +// ref: false +// error: 3:7-3:18 unknown variable +// error: 6:17-6:20 aligned axis + +// Invalid paper. +[page nonexistant] + +// Aligned axes. +[page main-dir: ltr] diff --git a/tests/typ/func-rgb.typ b/tests/typ/func-rgb.typ index 96c23ebdf..e88b19bf1 100644 --- a/tests/typ/func-rgb.typ +++ b/tests/typ/func-rgb.typ @@ -1,4 +1,11 @@ // Test the `rgb` function. +// +// warning: 9:6-9:9 must be between 0.0 and 1.0 +// warning: 9:11-9:15 must be between 0.0 and 1.0 +// error: 12:6-12:10 missing argument: blue component +// error: 15:5-15:5 missing argument: red component +// error: 15:5-15:5 missing argument: green component +// error: 15:5-15:5 missing argument: blue component // Check the output. [rgb 0.0, 0.3, 0.7] @@ -14,10 +21,3 @@ // Missing all components. [rgb] - -// warning: 10:6-10:9 must be between 0.0 and 1.0 -// warning: 10:11-10:15 must be between 0.0 and 1.0 -// error: 13:6-13:10 missing argument: blue component -// error: 16:5-16:5 missing argument: red component -// error: 16:5-16:5 missing argument: green component -// error: 16:5-16:5 missing argument: blue component diff --git a/tests/typeset.rs b/tests/typeset.rs index e586ae1a4..554c91497 100644 --- a/tests/typeset.rs +++ b/tests/typeset.rs @@ -27,8 +27,8 @@ use typst::typeset; const TYP_DIR: &str = "typ"; const REF_DIR: &str = "ref"; -const PNG_DIR: &str = "out/png"; -const PDF_DIR: &str = "out/pdf"; +const PNG_DIR: &str = "png"; +const PDF_DIR: &str = "pdf"; const FONT_DIR: &str = "../fonts"; fn main() { @@ -76,8 +76,26 @@ fn main() { let png_path = Path::new(PNG_DIR).join(&name).with_extension("png"); let pdf_path = Path::new(PDF_DIR).join(&name).with_extension("pdf"); let ref_path = Path::new(REF_DIR).join(&name).with_extension("png"); + ok &= test( + &name, + &src_path, + &png_path, + &pdf_path, + Some(&ref_path), + &env, + ); + } - ok &= test(&name, &src_path, &pdf_path, &png_path, &ref_path, &env); + let playground = Path::new("playground.typ"); + if playground.exists() { + test( + "playground", + playground, + Path::new("playground.png"), + Path::new("playground.pdf"), + None, + &env, + ); } if !ok { @@ -118,38 +136,73 @@ impl TestFilter { fn test( name: &str, src_path: &Path, - pdf_path: &Path, png_path: &Path, - ref_path: &Path, + pdf_path: &Path, + ref_path: Option<&Path>, env: &SharedEnv, ) -> bool { println!("Testing {}.", name); let src = fs::read_to_string(src_path).unwrap(); - let map = LineMap::new(&src); - let (ref_diags, compare_ref) = parse_metadata(&src, &map); + + let mut ok = true; + let mut frames = vec![]; + + for (i, part) in src.split("---").enumerate() { + let (part_ok, part_frames) = test_part(i, part, env); + ok &= part_ok; + frames.extend(part_frames); + } + + let env = env.borrow(); + if !frames.is_empty() { + let canvas = draw(&frames, &env, 2.0); + canvas.pixmap.save_png(png_path).unwrap(); + + let pdf_data = pdf::export(&frames, &env); + fs::write(pdf_path, pdf_data).unwrap(); + + if let Some(ref_path) = ref_path { + if let Ok(ref_pixmap) = Pixmap::load_png(ref_path) { + if canvas.pixmap != ref_pixmap { + println!(" Does not match reference image. ❌"); + ok = false; + } + } else { + println!(" Failed to open reference image. ❌"); + ok = false; + } + } + } + + if ok { + println!("\x1b[1ATesting {}. ✔", name); + } + + ok +} + +fn test_part(i: usize, src: &str, env: &SharedEnv) -> (bool, Vec) { + let (src, compare_ref, map, ref_diags) = parse_metadata(&src, i); let mut state = State::default(); state.page.size = Size::uniform(Length::pt(120.0)); state.page.margins = Sides::uniform(Some(Length::pt(10.0).into())); let Pass { - output: frames, + output: mut frames, feedback: Feedback { mut diags, .. }, } = typeset(&src, Rc::clone(env), state); + + if !compare_ref { + frames.clear(); + } + diags.sort_by_key(|d| d.span); - let env = env.borrow(); - let canvas = draw(&frames, &env, 2.0); - canvas.pixmap.save_png(png_path).unwrap(); - - let pdf_data = pdf::export(&frames, &env); - fs::write(pdf_path, pdf_data).unwrap(); - let mut ok = true; - if diags != ref_diags { - println!(" Does not match expected diagnostics. ❌"); + println!(" Subtest {} does not match expected diagnostics. ❌", i); ok = false; for diag in &diags { @@ -167,57 +220,61 @@ fn test( } } - if compare_ref { - if let Ok(ref_pixmap) = Pixmap::load_png(&ref_path) { - if canvas.pixmap != ref_pixmap { - println!(" Does not match reference image. ❌"); - ok = false; - } - } else { - println!(" Failed to open reference image. ❌"); - ok = false; - } - } - - if ok { - println!("\x1b[1ATesting {}. ✔", name); - } - - ok + (ok, frames) } -fn parse_metadata(src: &str, map: &LineMap) -> (SpanVec, bool) { +fn parse_metadata(src: &str, i: usize) -> (&str, bool, LineMap, SpanVec) { let mut diags = vec![]; let mut compare_ref = true; - for line in src.lines() { - compare_ref &= !line.starts_with("// compare-ref: false"); + let mut s = Scanner::new(src); + for k in 0 .. { + // Allow a newline directly after "---" (that is, if i > 0 and k == 0). + if !(i > 0 && k == 0) && !s.rest().starts_with("//") { + break; + } - let (level, rest) = if let Some(rest) = line.strip_prefix("// error: ") { - (Level::Error, rest) - } else if let Some(rest) = line.strip_prefix("// warning: ") { + let line = s.eat_until(typst::parse::is_newline); + s.eat_merging_crlf(); + + compare_ref &= !line.starts_with("// ref: false"); + + let (level, rest) = if let Some(rest) = line.strip_prefix("// warning: ") { (Level::Warning, rest) + } else if let Some(rest) = line.strip_prefix("// error: ") { + (Level::Error, rest) } else { continue; }; - fn pos(s: &mut Scanner, map: &LineMap) -> Pos { - let (line, _, column) = (num(s), s.eat_assert(':'), num(s)); - map.pos(Location { line, column }).unwrap() - } - - fn num(s: &mut Scanner) -> u32 { - s.eat_while(|c| c.is_numeric()).parse().unwrap() - } - - let mut s = Scanner::new(rest); - let (start, _, end) = (pos(&mut s, map), s.eat_assert('-'), pos(&mut s, map)); - diags.push(Diag::new(level, s.rest().trim()).with_span(start .. end)); + diags.push((level, rest)); } + let src = s.rest(); + let map = LineMap::new(src); + + let mut diags: Vec<_> = diags + .into_iter() + .map(|(level, rest)| { + fn pos(s: &mut Scanner, map: &LineMap) -> Pos { + let (line, _, column) = (num(s), s.eat_assert(':'), num(s)); + map.pos(Location { line, column }).unwrap() + } + + fn num(s: &mut Scanner) -> u32 { + s.eat_while(|c| c.is_numeric()).parse().unwrap() + } + + let mut s = Scanner::new(rest); + let (start, _, end) = + (pos(&mut s, &map), s.eat_assert('-'), pos(&mut s, &map)); + Diag::new(level, s.rest().trim()).with_span(start .. end) + }) + .collect(); + diags.sort_by_key(|d| d.span); - (diags, compare_ref) + (src, compare_ref, map, diags) } fn print_diag(diag: &Spanned, map: &LineMap) {