Skip to content
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

Add snapshot testing with insta #157

Open
wants to merge 1 commit into
base: oxidize
Choose a base branch
from
Open
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
256 changes: 256 additions & 0 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,5 @@ headers = "0.2"
hyper = "0.12"
tempfile = "^3.1"
tokio = "0.1.22"
insta = "0.11.0"
pretty-hex = "0.1.1"
Binary file added tests/assets/bmp_1bpp-332x240.bmp
Binary file not shown.
Binary file added tests/assets/bmp_24bpp-323x240.bmp
Binary file not shown.
Binary file added tests/assets/bmp_rle4-encoded-320x240.bmp
Binary file not shown.
1 change: 1 addition & 0 deletions tests/snapshots/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.preview.pdf
6 changes: 6 additions & 0 deletions tests/snapshots/bmp_images.tex
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
% Selections from the BMP test suite at http://bmptestsuite.sourceforge.net/
% scaled so there are no overfull boxes
\XeTeXpicfile bmp_1bpp-332x240.bmp scaled 1 width 33.2mm height 24.0mm
\XeTeXpicfile bmp_24bpp-323x240.bmp scaled 1 width 32.3mm height 24.0mm
\XeTeXpicfile bmp_rle4-encoded-320x240.bmp scaled 1 width 32.0mm height 24.0mm
\bye
8 changes: 8 additions & 0 deletions tests/snapshots/tex_outputs__bmp_images.log.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
source: tests/tex-outputs.rs
expression: string
---
**
(bmp_images.tex [1] )
Output written on bmp_images.xdv (1 page, 436 bytes).

17,712 changes: 17,712 additions & 0 deletions tests/snapshots/tex_outputs__bmp_images.pdf.snap

Large diffs are not rendered by default.

33 changes: 33 additions & 0 deletions tests/snapshots/tex_outputs__bmp_images.xdv.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
---
source: tests/tex-outputs.rs
expression: string
---
Length: 436 (0x1b4) bytes
0000: f7 07 01 83 92 c0 1c 3b 00 00 00 00 03 e8 08 74 .......;.......t
0010: 65 63 74 6f 6e 69 63 8b 00 00 00 01 00 00 00 00 ectonic.........
0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0040: ff ff ff ff ef 14 70 64 66 3a 70 61 67 65 73 69 ......pdf:pagesi
0050: 7a 65 20 64 65 66 61 75 6c 74 8d 9f f2 00 00 8e ze default......
0060: a0 02 83 33 da 8d a0 fd c1 15 85 ef 46 70 64 66 ...3........Fpdf
0070: 3a 69 6d 61 67 65 20 6d 61 74 72 69 78 20 30 2e :image matrix 0.
0080: 31 20 30 2e 30 20 30 2e 30 20 30 2e 31 20 30 2e 1 0.0 0.0 0.1 0.
0090: 30 20 30 2e 30 20 70 61 67 65 20 30 20 28 62 6d 0 0.0 page 0 (bm
00a0: 70 5f 31 62 70 70 2d 33 33 32 78 32 34 30 2e 62 p_1bpp-332x240.b
00b0: 6d 70 29 a4 44 49 5f ef 47 70 64 66 3a 69 6d 61 mp).DI_.Gpdf:ima
00c0: 67 65 20 6d 61 74 72 69 78 20 30 2e 31 20 30 2e ge matrix 0.1 0.
00d0: 30 20 30 2e 30 20 30 2e 31 20 30 2e 30 20 30 2e 0 0.0 0.1 0.0 0.
00e0: 30 20 70 61 67 65 20 30 20 28 62 6d 70 5f 32 34 0 page 0 (bmp_24
00f0: 62 70 70 2d 33 32 33 78 32 34 30 2e 62 6d 70 29 bpp-323x240.bmp)
0100: a1 ef 4e 70 64 66 3a 69 6d 61 67 65 20 6d 61 74 ..Npdf:image mat
0110: 72 69 78 20 30 2e 31 20 30 2e 30 20 30 2e 30 20 rix 0.1 0.0 0.0
0120: 30 2e 31 20 30 2e 30 20 30 2e 30 20 70 61 67 65 0.1 0.0 0.0 page
0130: 20 30 20 28 62 6d 70 5f 72 6c 65 34 2d 65 6e 63 0 (bmp_rle4-enc
0140: 6f 64 65 64 2d 33 32 30 78 32 34 30 2e 62 6d 70 oded-320x240.bmp
0150: 29 8e 9f 18 00 00 8d 92 00 e8 60 a3 f3 00 4b f1 ).........`...K.
0160: 60 79 00 0a 00 00 00 0a 00 00 00 05 63 6d 72 31 `y..........cmr1
0170: 30 ab 31 8e 8c f8 00 00 00 17 01 83 92 c0 1c 3b 0.1............;
0180: 00 00 00 00 03 e8 02 9b 33 da 01 d5 c1 47 00 02 ........3....G..
0190: 00 01 f3 00 4b f1 60 79 00 0a 00 00 00 0a 00 00 ....K.`y........
01a0: 00 05 63 6d 72 31 30 f9 00 00 01 75 07 df df df ..cmr10....u....
01b0: df df df df ....
124 changes: 123 additions & 1 deletion tests/tex-outputs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use tectonic::{TexEngine, XdvipdfmxEngine};

#[path = "util/mod.rs"]
mod util;
use crate::util::{ensure_plain_format, test_path, ExpectedInfo};
use crate::util::{ensure_plain_format, test_path, ExpectedInfo, OutputSnapshotChecker, OutputEncoding};

struct TestCase {
stem: String,
Expand Down Expand Up @@ -65,6 +65,118 @@ impl TestCase {
self.expect(Err(ErrorKind::Msg(msg.to_owned()).into()))
}

fn snap(&mut self) {
util::set_test_root();
let expect_xdv = self.expected_result.is_ok();

let mut p = test_path(&[]);

// IoProvider for the format file; with magic to generate the format
// on-the-fly if needed.
let mut fmt =
SingleInputFileIo::new(&ensure_plain_format().expect("couldn't write format file"));

// Set up some useful paths, and the IoProvider for the primary input file.
p.push("snapshots");
p.push(&self.stem);
p.set_extension("tex");
let texname = p.file_name().unwrap().to_str().unwrap().to_owned();
let mut tex = FilesystemPrimaryInputIo::new(&p);

p.set_extension("xdv");
let xdvname = p.file_name().unwrap().to_str().unwrap().to_owned();

p.set_extension("pdf");
let pdfname = p.file_name().unwrap().to_str().unwrap().to_owned();

// MemoryIo layer that will accept the outputs.
let mut mem = MemoryIo::new(true);

// We only need the assets when running xdvipdfmx, but due to how
// ownership works with IoStacks, it's easier to just unconditionally
// add this layer.
let mut assets = FilesystemIo::new(&test_path(&["assets"]), false, false, HashSet::new());

// Run the engine(s)!
let res = {
let mut io_list: Vec<&mut dyn IoProvider> =
vec![&mut mem, &mut tex, &mut fmt, &mut assets];
for io in &mut self.extra_io {
io_list.push(&mut **io);
}
let mut io = IoStack::new(io_list);

let mut events = NoopIoEventBackend::new();
let mut status = NoopStatusBackend::new();

let tex_res =
TexEngine::new().process(&mut io, &mut events, &mut status, "plain.fmt", &texname);

if self.check_pdf && tex_res.definitely_same(&Ok(TexResult::Spotless)) {
// While the xdv and log output is deterministic without setting
// SOURCE_DATE_EPOCH, xdvipdfmx uses the current date in various places.
env::set_var("SOURCE_DATE_EPOCH", "1456304492"); // TODO: default to deterministic behaviour

XdvipdfmxEngine::new()
.with_compression(false)
.with_deterministic_tags(true)
.process(&mut io, &mut events, &mut status, &xdvname, &pdfname)
.unwrap();
}

tex_res
};

// Check that outputs match expectations.

let files = mem.files.borrow();
let p = &mut p;

use OutputEncoding::*;

let mut snap = |ext: &str, enc: OutputEncoding| {
let checker = OutputSnapshotChecker::new(p, ext, enc);
let (name, string) = checker.snapshot(&files);
insta::assert_snapshot!(name, string);
};

snap("log", OutputEncoding::Utf8);

if !res.definitely_same(&self.expected_result) {
panic!(format!(
"expected TeX result {:?}, got {:?}",
self.expected_result, res
));
}

if expect_xdv {
snap("xdv", OutputEncoding::Binary);
}

if self.check_synctex {
snap("synctex.gz", OutputEncoding::Binary);
}

if self.check_pdf {
snap("pdf", OutputEncoding::Binary);
// we now write out a .preview.pdf file so you can visually check the PDF
// in addition to seeing a hexdump

use std::fs::File;
use std::io::prelude::*;

let mut path = test_path(&["snapshots"]);
path.push(&self.stem);
path.set_extension("pdf");
let name = path.file_name().unwrap();
let buf = files.get(name).unwrap();

path.set_extension("preview.pdf");
let mut file = File::create(&path).unwrap();
file.write_all(buf);
}
}

fn go(&mut self) {
util::set_test_root();

Expand Down Expand Up @@ -259,3 +371,13 @@ fn tectoniccodatokens_ok() {
fn the_letter_a() {
TestCase::new("the_letter_a").check_pdf(true).go()
}

mod snapshots {
use super::TestCase;
#[test]
fn bmp_images() {
TestCase::new("bmp_images")
.check_pdf(true)
.snap()
}
}
71 changes: 71 additions & 0 deletions tests/util/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,3 +211,74 @@ impl ExpectedInfo {
}
}
}

pub enum OutputEncoding {
Binary,
Utf8,
}

pub struct OutputSnapshotChecker {
name: OsString,
encoding: OutputEncoding,
gzipped: bool,
}

impl OutputSnapshotChecker {
fn create(path: &impl AsRef<Path>, encoding: OutputEncoding) -> Self{
let path = path.as_ref();
let name = path
.file_name()
.unwrap_or_else(|| panic!("couldn't get file name of {:?}", path))
.to_owned();
OutputSnapshotChecker {
name,
encoding,
gzipped: false
}
}

pub fn new(pbase: &mut PathBuf, ext: &str, enc: OutputEncoding) -> Self {
pbase.set_extension(ext);
OutputSnapshotChecker::create(pbase, enc)
}

pub fn new_gz(pbase: &mut PathBuf, ext: &str, enc: OutputEncoding) -> Self {
let mut neu = OutputSnapshotChecker::new(pbase, ext, enc);
neu.gzipped = true;
neu
}

fn get_snapshot_string(&self, observed: &[u8]) -> String {
match self.encoding {
OutputEncoding::Utf8 => {
let st = String::from_utf8(observed.to_vec()).unwrap();
st
}
OutputEncoding::Binary => {
let hex = pretty_hex::pretty_hex(&observed);
hex
}
}
}

pub fn snapshot<'a>(&'a self, files: &HashMap<OsString, Vec<u8>>) -> (&'a str, String) {
let name = self.name.to_str().unwrap();
let string = if !self.gzipped {
if let Some(data) = files.get(&self.name) {
self.get_snapshot_string(data)
} else {
panic!(
"{:?} not in {:?}",
self.name,
files.keys().collect::<Vec<_>>()
)
}
} else {
let mut buf = Vec::new();
let mut dec = GzDecoder::new(&files.get(&self.name).unwrap()[..]);
dec.read_to_end(&mut buf).unwrap();
self.get_snapshot_string(&buf)
};
(name, string)
}
}