From c29558e8d165df1d3fc1cb32079da9e40f957dc6 Mon Sep 17 00:00:00 2001 From: Martin Fischer Date: Tue, 30 Nov 2021 18:10:53 +0100 Subject: spans: add span tests --- Cargo.toml | 1 + src/lib.rs | 2 +- src/spans.rs | 21 +++++++++++----- tests/span-tests/demo.html | 1 + tests/span-tests/demo.out | 7 ++++++ tests/test_spans.rs | 63 ++++++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 88 insertions(+), 7 deletions(-) create mode 100644 tests/span-tests/demo.html create mode 100644 tests/span-tests/demo.out create mode 100644 tests/test_spans.rs diff --git a/Cargo.toml b/Cargo.toml index a9cb994..13f8e5d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -11,6 +11,7 @@ version = "0.2.1" include = ["src/**/*", "LICENSE", "README.md"] [dev-dependencies] +codespan-reporting = "0.11.1" pretty_assertions = "1.0.0" serde = { version = "1.0.130", features = ["derive"] } serde_json = "1.0.71" diff --git a/src/lib.rs b/src/lib.rs index 40f9588..4b74d61 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,7 +9,7 @@ mod error; mod machine; mod never; mod reader; -mod spans; +pub mod spans; mod tokenizer; mod utils; diff --git a/src/spans.rs b/src/spans.rs index 9d02940..72b30c0 100644 --- a/src/spans.rs +++ b/src/spans.rs @@ -1,3 +1,4 @@ +//! Source code spans. use std::{ collections::{BTreeSet, VecDeque}, marker::PhantomData, @@ -8,13 +9,18 @@ use crate::{Doctype, Emitter, EndTag, Error, Reader, StartTag, Token}; type Span = std::ops::Range; +/// A trait to be implemented by readers that track their own position. pub trait GetPos { + /// Returns the byte index of the current position. fn get_pos(&self) -> usize; } -struct PosTracker { - reader: R, - position: usize, +/// Wraps a [`Reader`] so that it implements [`GetPos`]. +pub struct PosTracker { + /// The wrapped reader. + pub reader: R, + /// The current position. + pub position: usize, } impl GetPos for PosTracker { @@ -145,12 +151,15 @@ impl Emitter for SpanEmitter { fn init_start_tag(&mut self, reader: &R) { self.current_token = Some(Token::StartTag(StartTag { - name_span: reader.get_pos()..reader.get_pos(), + name_span: reader.get_pos() - 1..reader.get_pos() - 1, ..Default::default() })); } - fn init_end_tag(&mut self, _reader: &R) { - self.current_token = Some(Token::EndTag(Default::default())); + fn init_end_tag(&mut self, reader: &R) { + self.current_token = Some(Token::EndTag(EndTag { + name_span: reader.get_pos() - 1..reader.get_pos() - 1, + ..Default::default() + })); self.seen_attributes.clear(); } diff --git a/tests/span-tests/demo.html b/tests/span-tests/demo.html new file mode 100644 index 0000000..900f74c --- /dev/null +++ b/tests/span-tests/demo.html @@ -0,0 +1 @@ +this is a tag:

test

diff --git a/tests/span-tests/demo.out b/tests/span-tests/demo.out new file mode 100644 index 0000000..98d6766 --- /dev/null +++ b/tests/span-tests/demo.out @@ -0,0 +1,7 @@ +note: + ┌─ test.html:1:17 + │ +1 │ this is a tag:

test

+ │ ^^ ^^ end tag + │ │ + │ start tag diff --git a/tests/test_spans.rs b/tests/test_spans.rs new file mode 100644 index 0000000..a3d1c96 --- /dev/null +++ b/tests/test_spans.rs @@ -0,0 +1,63 @@ +use std::include_str; + +use codespan_reporting::{ + self, + diagnostic::{Diagnostic, Label}, + files::SimpleFiles, + term::{self, termcolor::Buffer}, +}; +use html5gum::{ + spans::{PosTracker, SpanEmitter}, + Readable, Token, Tokenizer, +}; + +#[test] +fn test() { + let html = include_str!("span-tests/demo.html"); + + let mut files = SimpleFiles::new(); + let file_id = files.add("test.html", html); + let mut labels = Vec::new(); + + for token in Tokenizer::new_with_emitter( + PosTracker { + reader: html.to_reader(), + position: 0, + }, + SpanEmitter::default(), + ) + .infallible() + { + if let Token::StartTag(tag) = token { + labels.push(Label::primary(file_id, tag.name_span).with_message("start tag")); + } else if let Token::EndTag(tag) = token { + labels.push(Label::primary(file_id, tag.name_span).with_message("end tag")); + } + } + + let diagnostic = Diagnostic::note().with_labels(labels); + + let mut writer = Buffer::no_color(); + let config = codespan_reporting::term::Config::default(); + term::emit(&mut writer, &config, &files, &diagnostic).unwrap(); + + let actual = remove_trailing_spaces(std::str::from_utf8(writer.as_slice()).unwrap()); + let expected = include_str!("span-tests/demo.out"); + + if actual != expected { + println!( + "EXPECTED:\n{banner}\n{expected}{banner}\n\nACTUAL OUTPUT:\n{banner}\n{actual}{banner}", + banner = "-".repeat(30), + expected = expected, + actual = actual + ); + panic!("failed"); + } +} + +fn remove_trailing_spaces(text: &str) -> String { + text.lines() + .map(|l| l.trim_end()) + .collect::>() + .join("\n") +} -- cgit v1.2.3